为什么boost :: scoped_ptr在继承场景中不起作用?

Emi*_*ile 1 c++ polymorphism inheritance boost

使用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;
}
Run Code Online (Sandbox Code Playgroud)

你能解释一下吗?是否存在不使用scoped_ptr作为多态成员变量的"黄金法则"?

In *_*ico 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);
    // ....
};
Run Code Online (Sandbox Code Playgroud)

当这个构造函数reset()被调用时,shared_ptr"记住"原始类型,Y这样当引用计数变为零时,它将delete正确调用.(当然p必须可转换为T.)这在文档中明确说明:

[此构造函数已更改为模板,以便记住传递的实际指针类型.析构函数将使用相同的指针调用delete,完成其原始类型,即使T没有虚拟析构函数,或者为void....]

scoped_ptr构造reset()看起来像这样:

template<class T>
class scoped_ptr : noncopyable
{
public:
    // ...
    explicit scoped_ptr(T * p = 0);
    // ...
    void reset(T * p = 0);
};
Run Code Online (Sandbox Code Playgroud)

所以没有办法scoped_ptr"记住"原始类型是什么.当它到达delete指针时,它基本上是这样做的:

delete this->get();
Run Code Online (Sandbox Code Playgroud)

scoped_ptr<T>::get()返回一个T*.因此,如果scoped_ptr指向不是T但实际上是子类的东西T,则需要实现virtual析构函数:

class Base
{
public:
    Base() { cout << "Base constructor" << endl ; }
    virtual ~Base() { cout << "Base destructor" << endl; }
};
Run Code Online (Sandbox Code Playgroud)

那么为什么不scoped_ptr为这种情况实现特殊的构造函数shared_ptr呢?因为"scoped_ptr模板是简单需求的简单解决方案".shared_ptr做了大量的簿记来实现其广泛的功能.请注意,intrusive_ptr也不会"记住"指针的原始类型,因为它意味着尽可能轻量级(一个指针).

  • 您关于"为什么"的其他信息使其成为一个很好的答案.它提供了我需要的信息.谢谢! (2认同)

Seb*_*ach 8

与之不同shared_ptr<>,scoped_ptr<>不会"记住"传递给构造函数的确切类型.该http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptr.htm 简介讲述:

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
Run Code Online (Sandbox Code Playgroud)

};

即它无法知道你究竟通过了什么,它只知道T,Base在你的情况下.为了能够正确删除,您需要使用shared_ptr<Base>它是否适合您的设计,或者您必须拥有Base虚拟析构函数

class Base
{
public:
    Base() { cout << "Base constructor" << endl ; }
    virtual ~Base() { cout << "Base destructor" << endl; }
};
Run Code Online (Sandbox Code Playgroud)

根据经验(参见Meyers):

如果要通过基类以多态方式删除,请使基类析构函数为虚拟.

不像scoped_ptr<>,shared_ptr<> 明确记住传递给构造函数的指针类型:

...
template<class Y> shared_ptr(shared_ptr<Y> const & r);
...
Run Code Online (Sandbox Code Playgroud)

而医生说

此构造函数已更改为模板,以便记住传递的实际指针类型.析构函数将使用相同的指针调用delete,完成其原始类型,即使T没有虚拟析构函数,或者为void.

这可以通过混合运行时与静态多态来实现.