为什么boost::scoped_ptr不能在继承场景中工作

Why does boost::scoped_ptr not work in inheritance scenario?

本文关键字:继承 工作 不能 boost scoped ptr 为什么      更新时间:2023-10-16

使用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多态成员变量?

它之所以适用于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。)这在文档中有明确的说明:

为了便于记忆,这个构造函数被改成了模板传递的实际指针类型。析构函数将调用delete相同的指针,完整的原始类型,即使当T没有虚析构函数,或者是void. ...]

scoped_ptrreset()的构造函数是这样的:

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

所以scoped_ptr没有办法"记住"原始类型是什么。当需要delete指针时,它实际上是这样做的:

delete this->get();

scoped_ptr<T>::get()返回一个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模板是满足简单需求的简单解决方案"。shared_ptr做了大量的记账来实现其广泛的功能。请注意,intrusive_ptr也不"记住"指针的原始类型,因为它意味着尽可能轻量级(一个指针)。

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

};

。它不知道你传递了什么,它只知道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<>显式地记住传递给构造函数的指针类型:

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

而文档说

为了记住传递的实际指针类型,这个构造函数被改成了模板。即使在T没有虚析构函数或为void时,析构函数也将使用相同的指针调用delete,并完成其原始类型。

这可以通过混合运行时多态性和静态多态性来实现。

为了通过指向基类的指针调用派生类的析构函数,需要有一个虚析构函数