使用boost :: scoped_ptr来保存引用时,不会调用派生对象的析构函数。它在使用boost :: shared_ptr。
时会发生#include "stdafx.h"
#include <iostream>
#include "boost/scoped_ptr.hpp"
#include "boost/shared_ptr.hpp"
using namespace std;
class Base
{
public:
Base() { cout << "Base constructor" << endl ; }
~Base() { cout << "Base destructor" << endl; }
};
class Derived : public Base
{
public:
Derived() : Base() { cout << "Derived constructor" << endl; }
~Derived() { cout << "Derived destructor" << endl; }
};
int _tmain(int argc, _TCHAR* argv[])
{
boost::scoped_ptr<Base> pb; // replacing by shared_ptr does call Derived destructor
pb.reset(new Derived());
cout << "Program ends here" << endl;
}
你能解释一下吗?是否存在不使用scoped_ptr作为多态成员变量的“黄金法则”?
答案 0 :(得分:18)
它适用于shared_ptr
的原因是因为它实现了一个特殊的构造函数和reset()
方法,如下所示:
template<class T>
class shared_ptr
{
public:
// ...
// Note use of template
template<class Y> explicit shared_ptr(Y * p);
// ....
// Note use of template
template<class Y> void reset(Y * p);
// ....
};
当调用此构造函数或reset()
时,shared_ptr
会“记住”原始类型Y
,以便当引用计数变为零时,它将正确调用delete
。 (当然p
必须可转换为T
。)This is explicitly stated in the documentation:
[此构造函数已更改为模板以便记住 传递的实际指针类型。析构函数将调用delete 相同的指针,完整的原始类型,即使是T 没有虚拟析构函数,或者是无效的。 ...]
scoped_ptr
constructor和reset()
如下所示:
template<class T>
class scoped_ptr : noncopyable
{
public:
// ...
explicit scoped_ptr(T * p = 0);
// ...
void reset(T * p = 0);
};
因此scoped_ptr
无法“记住”原始类型是什么。当需要delete
指针it essentially does this:
delete this->get();
scoped_ptr<T>::get()
returns a T*
。因此,如果scoped_ptr
指向不是T
但实际上是T
的子类的内容,则需要实现virtual
析构函数:
class Base
{
public:
Base() { cout << "Base constructor" << endl ; }
virtual ~Base() { cout << "Base destructor" << endl; }
};
那么scoped_ptr
为什么不shared_ptr
为这种情况实现特殊的构造函数呢?因为"scoped_ptr template is a simple solution for simple needs"。 shared_ptr
执行大量记账以实现其广泛的功能。请注意,intrusive_ptr
也不会“记住”指针的原始类型,因为它意味着尽可能轻量级(一个指针)。
答案 1 :(得分:8)
与shared_ptr<>
不同,scoped_ptr<>
不会“记住”传递给其构造函数的确切类型。 http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptr.htm synopsis告诉我们:
template<class T> class scoped_ptr : noncopyable {
public:
typedef T element_type;
explicit scoped_ptr(T * p = 0); // never throws
~scoped_ptr(); // never throws
void reset(T * p = 0); // never throws
T & operator*() const; // never throws
T * operator->() const; // never throws
T * get() const; // never throws
operator unspecified-bool-type() const; // never throws
void swap(scoped_ptr & b); // never throws
};
即。它无法知道你究竟通过了什么,它只知道T
,在你的情况下是Base
。为了能够正确删除,您需要使用shared_ptr<Base>
,如果这符合您的设计,或者您必须让Base
拥有虚拟析构函数
class Base
{
public:
Base() { cout << "Base constructor" << endl ; }
virtual ~Base() { cout << "Base destructor" << endl; }
};
根据经验(参见Meyers):
如果要通过基类以多态方式删除,请将基类析构函数设为虚拟。
与scoped_ptr<>
,shared_ptr<>
explicitly remembers不同,您传递给构造函数的指针类型:
...
template<class Y> shared_ptr(shared_ptr<Y> const & r);
...
并且文档说
此构造函数已更改为模板,以便记住传递的实际指针类型。析构函数将使用相同的指针调用delete,并使用其原始类型调用,即使T没有虚拟析构函数,也无效。
通过混合运行时和静态多态来实现这一点。
答案 2 :(得分:4)
您需要有一个虚拟析构函数,以便通过指向其基类的指针调用派生类析构函数。