c++ - 递归函数调用中的数据成员更新问题
问题描述
我可能没有看到明显的。我有一个对象层次结构,并希望对层次结构中的所有几何对象应用变换。基类Node如下:
class Node
{
Matrix4 node_matrix;
// ... other stuff
}
它包含一个基线矩阵 node_matrix,由程序修改:
for (size_t a = 0; a < level.objects.size(); ++a)
{
ApplyTransform(level.objects[a].node_matrix);
level.objects[a].Update(level.objects[a].node_matrix);
// ... do more stuff
}
objects[a] 是一个 GameObject 和根节点,它的矩阵被 ApplyTransform() 修改。然后,我使用下面类中定义的递归函数 Update() 在根节点上调用 update 方法:
class GameObject : public Node
{
public:
GameObject *parent;
std::vector<GameObject*> children;
Matrix4 current_node_matrix;
GeometryNode *geometry;
//Default ctor
GameObject() : parent(nullptr) {}
//Copy ctor and assignment
GameObject(const GameObject&) = default;
GameObject& operator=(const GameObject&) = default;
//Move ctor and assignment
GameObject(GameObject &&) = default;
GameObject& operator=(GameObject &&) = default;
//Default dtor
~GameObject() = default;
void Update(Matrix4 nm)
{
if (parent != nullptr)
{
current_node_matrix = nm * node_matrix;
}
else
{
current_node_matrix = nm;
}
for (size_t i = 0; i < children.size(); ++i)
{
children[i]->Update(current_node_matrix);
}
}
};
这很好,但我认为我可以通过简单地编写来避免将矩阵传递给 Update() 方法;
void GameObject::Update()
{
if (parent != nullptr)
{
current_node_matrix = parent->current_node_matrix * node_matrix;
}
else
{
current_node_matrix = node_matrix;
}
for (size_t i = 0; i < children.size(); ++i)
{
children[i]->Update();
}
}
我验证了 current_node_matrix 在第一次调用(根节点)中确实按预期进行了修改,但是在使用时
parent->current_node_matrix
在递归调用中,孩子看到原始(未修改)矩阵。我验证了很多事情,只是不明白为什么 current_node_matrix 在父对象中正确更新但对子对象不可见(父指针指向正确的对象,我也检查过)。
我可能在房间里看不到大象。有什么想法吗?
我在下面添加了一个功能齐全的可重现示例。这与代码中的作用基本相同,并且以相同的方式失败:调用 Update1 有效,而调用 Update2 失败。
提前致谢,
克里斯
最小可重复的例子
#include <iostream>
#include <vector>
enum class Matrix4Type {identity};
class Matrix4
{
public:
float M[4][4]{0};
//Default ctor
Matrix4() = default;
Matrix4(Matrix4Type);
//Copy ctor and assignment
Matrix4(const Matrix4&) = default;
Matrix4& operator=(const Matrix4&) = default;
//Move ctor and assignment
Matrix4(Matrix4&&) = default;
Matrix4& operator=(Matrix4&&) = default;
friend Matrix4 operator*(const Matrix4& mult1, const Matrix4& mult2);
Matrix4 operator*(const float scale);
friend std::ostream& operator<<(std::ostream &os, const Matrix4 &m);
};
Matrix4::Matrix4(Matrix4Type mtype)
{
switch (mtype)
{
case Matrix4Type::identity:
{
M[0][0] = 1.0f;
M[1][1] = 1.0f;
M[2][2] = 1.0f;
M[3][3] = 1.0f;
break;
}
}
}
Matrix4 operator*(const Matrix4& mult1, const Matrix4& mult2)
{
Matrix4 retmat;
for (uint8_t i = 0; i < 4; ++i)
{
for (uint8_t j = 0; j < 4; ++j)
{
for (uint8_t k = 0; k < 4; ++k)
{
retmat.M[i][j] += mult1.M[i][k] * mult2.M[k][j];
}
}
}
return retmat;
}
Matrix4 Matrix4::operator*(const float scale)
{
Matrix4 retmat = *this;
for (uint8_t i = 0; i < 4; ++i)
{
for (uint8_t j = 0; j < 4; ++j)
{
retmat.M[i][j] *= scale;
}
}
return retmat;
}
std::ostream& operator<<(std::ostream &os, const Matrix4 &m)
{
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
os << m.M[i][j] << " ";
}
os << std::endl;
}
os << "-------" << std::endl;
return os;
}
class Node
{
public:
Matrix4 node_matrix;
//Default ctor
Node() = default;
//Custom ctor
Node(const Matrix4 &m) : node_matrix(m) { }
//Copy ctor and assignment
Node(const Node &pt) = default;
Node& operator=(const Node &pt) = default;
//Move ctor and assignment
Node(Node &&mv) = default;
Node& operator=(Node &&mv) = default;
//Default dtor
~Node() = default;
inline void ApplyTransform(const Matrix4 &trans) noexcept
{
node_matrix = node_matrix * trans;
}
};
class GameObject : public Node
{
public:
GameObject *parent;
std::vector<GameObject*> children;
Matrix4 current_node_matrix;
//Default ctor
GameObject() : parent(nullptr) {}
//Copy ctor and assignment
GameObject(const GameObject&) = default;
GameObject& operator=(const GameObject&) = default;
//Move ctor and assignment
GameObject(GameObject &&) = default;
GameObject& operator=(GameObject &&) = default;
//Default ctor
~GameObject() = default;
void Update1(Matrix4 nm)
{
if (parent != nullptr)
{
std::cout << "Parent" << std::endl << nm << std::endl;
current_node_matrix = nm * node_matrix;
}
else
{
std::cout << "Root" << std::endl;
current_node_matrix = nm;
}
std::cout << current_node_matrix << std::endl;
for (size_t i = 0; i < children.size(); ++i)
{
children[i]->Update1(current_node_matrix);
}
}
void Update2()
{
if (parent != nullptr)
{
std::cout << "Parent" << std::endl << parent->current_node_matrix << std::endl;
current_node_matrix = parent->current_node_matrix * node_matrix;
}
else
{
std::cout << "Root" << std::endl;
current_node_matrix = node_matrix;
}
std::cout << current_node_matrix << std::endl;
for (size_t i = 0; i < children.size(); ++i)
{
children[i]->Update2();
}
}
void AddChild(GameObject *toadd)
{
toadd->parent = this;
children.push_back(toadd);
}
void SetCoordinateSystem(const Matrix4&m)
{
node_matrix = m;
}
};
int main(int argc, const char * argv[])
{
using std::cout;
using std::endl;
std::vector<GameObject> objects;
GameObject base1, child1, child2, second_child1;
Matrix4 temp1 = Matrix4(Matrix4Type::identity);
Matrix4 temp2 = Matrix4(Matrix4Type::identity) * 2.0f;
base1.SetCoordinateSystem(temp1);
child1.SetCoordinateSystem(temp1);
child2.SetCoordinateSystem(temp1);
second_child1.SetCoordinateSystem(temp1);
base1.AddChild(&child1);
base1.AddChild(&child2);
child2.AddChild(&second_child1);
objects.push_back(base1);
objects[0].ApplyTransform(temp2);
objects[0].Update1(objects[0].node_matrix);
//objects[0].Update2();
return 0;
}
解决方案
我已经查看了您的最小可复制示例。
Update2()
很好。
问题出在vector<GameObject> objects
. 当你这样做时objects.push_back(base1)
,而不是持有base1
你所期望的行为,会objects
复制一个新对象base1
,然后包含新对象。
要使用矢量容器,我建议您要么
- make
objects
包含指向游戏对象的指针 (vector<GameObject*> objects
) - 将游戏对象包含在
objects
.
推荐阅读
- android - Looper 线程问题?但不使用动画师
- php - 如何使用 HttpRequest.php 到 Laravel
- c# - 无法将跟踪事件记录到 Application Insights
- php - 五个独特的页面导致一页。我可以更改
- swift - UserDefaults.standard.set(any, forKey:) 写字典 SIGABRTs 时?
- linux - perf:为什么我没有“系统调用”计数器?
- javascript - 如何在 React 组件上放置未平面化的 svg 文件
- sparql - 如何使用 SPARQL 从 dbpedia 获取 'is dct:subject of' 属性
- django - 在 django admin 中更改显示顺序(数字)更容易?
- sql - sql server 使用公共列连接没有主键的表