2011-07-20 48 views
1

使用boost :: scoped_ptr来存放引用时,不会调用派生对象的析构函数。它使用boost :: shared_ptr。为什么boost :: scoped_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多态成员变量?

回答

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

【这个构造函数已更改为模板,以记住 传递的实际的指针类型。析构函数会调用 相同的指针删除,即使T的 没有虚拟析构函数,或者是void也是如此。 ...]

scoped_ptr constructorreset()是这样的:

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但实际上的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也不会“记住”指针的原始类型,因为它意味着尽可能轻量级(一个指针)。

+2

关于“为什么”的附加信息使得这是一个很好的答案。它提供了我需要的信息。谢谢! – Emile

4

您需要有一个虚拟析构函数才能通过指向其基类的指针调用派生类析构函数。

8

shared_ptr<>不同,scoped_ptr<>不会“记住”您传递给其构造函数的确切类型。该http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptr.htmsynopsis讲述:

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 

};

I.e.它不知道你通过了什么,它只知道T,你的情况是Base。为了使正确的删除,您可能需要使用shared_ptr<Base>是否会适合你的设计,或者您必须在Base有一个虚析构函数

class Base 
{ 
public: 
    Base() { cout << "Base constructor" << endl ; } 
    virtual ~Base() { cout << "Base destructor" << endl; } 
}; 

作为一个经验法则(见迈尔斯):

如果要通过基类以多态方式删除,使基类析构函数具有虚拟性。

不像scoped_ptr<>shared_ptr<>explicitly remembers你传递给构造函数的指针类型:

... 
template<class Y> shared_ptr(shared_ptr<Y> const & r); 
... 

和医生说

此构造已更改为模板,以记实际的指针类型通过。即使T没有虚拟析构函数,析构函数也会使用相同的指针调用delete,并完成其原始类型,或者是void。

这是通过将运行时与静态多态混合在一起而实现的。

+0

我怎么能忘记让析构函数变成虚拟的。感谢您解释为什么它与shared_ptr一起工作。 – Emile