我读过不完整类型的unique_ptr和Checked Delete。但是,当使用智能指针,或者至少是c++ 11智能指针的一个子集时,检查删除是否已经过时了?
取以下代码:
class A;
class B
{
public:
std::auto_ptr<A> autoPtr;
std::unique_ptr<A> uniquePtr;
std::shared_ptr<A> sharedPtr;
A* rawPtr;
B();
~B(){delete rawPtr;}
};
class A
{
public:
~A(){std::cout << "~A" << std::endl;}
};
B::B()
{
autoPtr = std::auto_ptr<A>(new A());
uniquePtr = std::unique_ptr<A>(new A());
sharedPtr = std::shared_ptr<A>(new A());
rawPtr = new A();
}
B b;
当定义了B的析构函数时,A的类型仍然是不完整的。据我所知[c++ 11标准@ [expr.delete]]原始指针的删除是未定义的行为。在我的机器上,gcc 4.8显示了一些关于这个的警告,但是编译和A的析构函数没有被正确调用。
但是智能指针呢?正如我所读到的,unique_ptr和shared_ptr必须根据c++11标准工作。但是两个链接的文档都声明auto_ptr不起作用。但至少在我的gcc 4.8中,auto_ptr也正确地调用析构函数,没有任何警告。这仍然是未定义的行为和gcc只是好吗?
简而言之:根据c++ 11标准,四个成员变量中哪一个保证使用稍后定义的析构函数适当地销毁其a指针?
最后:如果我只使用unique_ptr和shared_ptr,从不调用"删除"自己,我是安全的,永远不需要考虑"检查删除"了吗?
对于shared_ptr
,重要的是当您将指针传递给shared_ptr
时类型是否完整(通常应该是这样,因为您刚刚创建了A
)。此时,shared_ptr
将创建某种类型的删除器,它需要完整类型,并将其存储起来,以便在引用计数下降到零时可用(即使这发生在A
不完整的文件中)。
对于unique_ptr
,重要的是在调用default_delete
时类型是否完成,这通常在unique_ptr
析构函数中(但也可以在reset()
成员或赋值操作符中)。
对于不完全类型使用auto_ptr
是没有定义的。
回答题目中的问题:标准要求shared_ptr
和unique_ptr
将拒绝编译试图删除不完整类型的代码。这并不能保证100%的安全,因为如果base
没有虚析构函数,执行unique_ptr<base>(new derived)
仍然可能有未定义的行为。
我认为允许代码编译的原因是GCC在文件末尾实例化模板,并且在定义b
之前不需要内联B
析构函数,此时A
已经完成。如果你把A
和B::B
的定义与变量b
放在一个单独的文件中,那么你会发现当b
被销毁时,unique_ptr
不会正确地删除A
(事实上,它甚至不应该编译,因为销毁b
调用default_delete
,而标准说这需要一个完整的类型,否则程序是病态的)。
可移植的解决方案是确保在为B
定义析构函数的地方A
是完整的,因此,如果A
不是在所有文件中都完整,则不要内联地定义析构函数。