假设我有一个名为vertex的结构,其方法是添加两个顶点:
struct vertex {
float x, y, z;
// constructs the vertex with initial values
vertex(float ix, float iy, float iz);
// returns the value c = this + b
vertex operator+(vertex b);
};
vertex::vertex(float ix, float iy, float iz){
this->x = ix; this->y = iy; this->z = iz;
}
vertex vertex::operator+(vertex b){
vertex c;
c.x = this->x + b.x;
c.y = this->y + b.y;
c.z = this->z + b.z;
return c;
}
在另一个调用函数中,我想在一起添加两个顶点,并将结果添加到vector<vertex*>
。什么时候使用返回的值添加到给定的向量是否安全?如果从来没有,我将如何实现它?
例如,
vector<vertex*> v;
vertex a(1, 2, 3);
vertex b(4, 5, 6);
v.push_back(&(a + b));
答案 0 :(得分:2)
这是不安全的,因为您正在存储指向自动或临时变量的指针,当当前函数终止时,该变量将被回收。
将动态分配的对象与自动分配的对象混合是一个严重的风险。有时,最好的策略是完全禁止自动分配(例如,通过使构造函数为私有并使用工厂方法来创建新实例)。然后,您将负责在某些时候消除这些。
第二个选项(不一定是您想要的)是按值执行所有操作。有一个Vertex而不是Vertex *的矢量,只是在存储顶点时复制它们。编写类的方式,所有字段都是原语,因此这可能足够好,您不会遇到性能或深层复制语义问题。
答案 1 :(得分:1)
它永远不会保存,因为您向向量添加了指向临时对象的指针。在执行该行之后,该临时对象将被销毁,从而使向量具有无效指针。
你有两种可能性。要么不在向量中存储指针而是使用vector<vertex>
,要么在添加时显式分配临时对象的新副本:
v.push_back(new vertex(a+b));
答案 2 :(得分:1)
另一种选择是将智能指针插入到std容器中,例如boost:shared_ptr,因为共享指针将为您处理内存管理。但是,您需要从Shared_Ptr vertex :: operator +(顶点b)返回一个共享指针,这意味着返回值仍将在堆上。
如果您不熟悉boost:shared_ptr,那么只需按照其他帖子的建议按值执行所有操作。这实际上是标准做法。
答案 3 :(得分:1)
这不安全,因为要添加到向量的顶点值在堆上不。返回的顶点对象位于堆栈上,因此一旦当前函数终止,它们的内存位置可能会被覆盖。如果在当前函数结束后向量(或其副本)仍然存在,则无法保证其指针仍将引用有效的顶点对象。
这种情况最危险的部分是这些顶点对象在创建它们的函数结束后很久就可以在内存中存活。我曾经看过一个这样的例子,一个学生在一个非常长的构造函数中填充了一个带有指向值对象的向量。向量是对象中的成员字段,因此它在构造函数结束后仍然存在。因为构造函数太长了,所以向量指向的值对象直到不同函数的中途才被覆盖。在调试器中观察向量的内容会产生令人困惑的错觉,即对象在内存中会自发地破坏。
除非您确实需要将这些顶点存储为指针,否则最安全的策略是将它们作为值对象存储在向量中:vector<vertex>
而不是vector<vertex*>
。您仍然可以将不同的运营商联系在一起;实际上,将它们一起更改为值对象应该更容易,因为您不会经常需要取消引用指针。