我认为这是一个简单的问题,但我一直在盯着一些复杂的遗留代码,我再也看不到森林的树了。这个应用程序将运行数天,然后在退出时失败(当然它不适用于较短的工作!)我怀疑是一个SEGV。
我在下面用一些伪代码简化了这个案例(希望我做对了)。
在人类方面:我有一个类XYZ,它有很多东西,包括指向简单类ABC的指针向量(让我们假设它很简单)。这些指针在XYZ的析构函数中被删除;这就是所有析构函数。
然后我有一个简单的基类TheBase,它只有两个简单的虚方法,没有析构函数。
最后我有三个类,Tom和Dick(源自TheBase)和Harry(不是从TheBase派生的。)所有这三个类都是从对XYZ对象的const引用构造的。所以他们有一个对XYZ对象的const引用。他们也没有析构函数。
在主要内容中,为Tom,Dick和Harry对象中的每一个定义了boost :: shared_ptr。然后创建XYZ对象。接下来,XYZ对象作为const引用传递给Tom,Dick和Harry对象。在那之后,发生了大量的事情和主要的退出。
那么当所有这些事情超出范围时会发生什么?特别是XYZ对象?这会被正确处理吗?好像有些东西会被删除多次。
// A simple class (let's assume it is!)
class ABC
{
// unimportant stuff.
}
// class XYZ has an array of ABC objects. All the destructor does is delete those objects.
class XZY
{
public:
XYZ(vector<string> v1,
vector<string> v2,
vector<string> v3 );
virtual ~XYZ(){
for ( i = 0; i < n, i++ ){
delete my_abcs[i];
}
}
private:
vector <ABC*> my_abcs
// lots of other methods & members
}
// Simple base class with only 2 simple virtual methods
class TheBase
{
public:
virtual void minor_func1();
virtual void minor_func2();
}
// A class derived from base class. Constructs with a const reference to an XYZ class.
class Tom:TheBase
{
public:
Tom( const XYZ & xyz )
private:
const XYZ & my_xyz;
// lots of other methods & members
}
Tom::Tom(const XYZ & xyz):my_xyz(xyz){
...
}
// Another class derived from base class. Constructs with a const reference to an XYZ class.
class Dick:TheBase
{
public:
Dick( const XYZ & xyz )
private:
const XYZ & my_xyz;
// lots of other methods & members
}
Dick::Dick(const XYZ & xyz):my_xyz(xyz){
...
}
// A class NOT derived from base class but still constructs with a const reference to an XYZ class.
class Harry:TheBase
{
public:
Harry( const XYZ & xyz )
private:
const XYZ & my_xyz;
// lots of other methods & members
}
Harry::Harry(const XYZ & xyz):my_xyz(xyz){
...
}
main (...){
...
boost::shared_ptr <Tom> a_tom;
boost::shared_ptr <Dick> a_dick;
boost::shared_ptr <Harry> a_harry;
...
XYZ a_xyz( ... );
a_tom.reset( new Tom( a_xyz) );
a_dick.reset( new Dick( a_xyz) );
a_harry.reset( new harry( a_xyz) );
...
}
答案 0 :(得分:3)
shared_ptr
管理的对象将被破坏
指向它们的最后shared_ptr
被破坏。本地
变量在超出范围时被破坏
它们被创建的逆序。在你的情况下,如果有
没有shared_ptr
具有静态生命周期(或伪静态生命周期;
即在动态分配的对象中,它不是
破坏直到离开主要,如果有的话),然后a_xyz
将被破坏,然后由三个物体指向
shared_ptr
。 如果这些对象不使用引用
他们的析构者a_xyz
(shared_ptr
没有
复制到他们将比他们活得更长的地方,然后那里
应该没问题。
答案 1 :(得分:1)
我有一个类XYZ,它有很多东西,包括指向简单类ABC的指针向量(让我们假设它很简单)。这些指针在XYZ的析构函数中被删除;这就是所有析构函数。
顺便说一句,这个问题的答案是std::vector<std::unique_ptr<ABC>>
,它封装了std::vector
拥有类型中有问题的指针的事实,并且无需手动销毁它们。它还会阻止意外拷贝:如果你实现了一个非平凡的析构函数,你需要实现或阻止拷贝构造和拷贝分配(3的规则)。
使用std::vector<std::unique_ptr<ABC>>
时,只能移动它,因此移动分配和移动构造将被取消阻止,而复制构造和复制分配将被阻止。
std::unique_ptr<T>
有很少的开销。
当您需要访问基础.get()
时,唯一的成本是对T*
的大量调用,基本上没有运行时成本。
在主要内容中,为Tom,Dick和Harry对象中的每一个定义了boost :: shared_ptr。然后创建XYZ对象。接下来,XYZ对象作为const引用传递给Tom,Dick和Harry对象。在那之后,发生了大量的事情和主要的退出。
同一范围内的C ++中的对象将按照声明它们的相反顺序销毁。因此,汤姆,迪克和哈利的生命将比XYZ对象更长。
最后我有三个类,Tom和Dick(源自TheBase)和Harry(不是从TheBase派生的。)所有这三个类都是从对XYZ对象的const引用构造的。所以他们有一个对XYZ对象的const引用。他们也没有析构函数。
引用(在此上下文中)对引用事物的生命周期没有影响。引用不是智能指针,它们是不安全和未经检查的别名。通常,当您创建对某事物的引用时,确保该对象持续的时间长于对象的引用是您的工作。
如果XYZ
超出范围,Tom
,Dick
或Harry
中的任何一个访问其引用到XYZ
,那么您已调用undefined行为。如果你不这样做,你就没有。
即使你没有,依靠这个也是一个坏习惯,因为你的代码会非常脆弱。
(要清楚:当我说“在这种情况下”时,我的意思是。有一个上下文,其中参考生命周期改变了有问题的对象的生命周期:何时直接构造引用 从一个临时(匿名)对象,该匿名对象的生命周期将扩展到引用的生命周期。但是,请注意,以这种方式间接构造的引用不具有此属性 - 所以{{1} }会延长匿名A& a = A();
的生命周期,而A
则不会,B& b = a;
不起作用,但A& get_A() { return A(); }; A& a = get_A();
会导致终身延期(不确定最后一个)。)