簡體   English   中英

為什么 boost::scoped_ptr 在 inheritance 方案中不起作用?

[英]Why does boost::scoped_ptr not work in inheritance scenario?

當使用 boost::scoped_ptr 保存引用時,不會調用派生的 object 的析構函數。 使用 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_ptr構造函數reset()如下所示:

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.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

};

即它不知道你到底通過了什么,它只知道T ,在你的情況下是Base 為了啟用正確的刪除,您需要使用shared_ptr<Base>如果這適合您的設計,或者您必須讓您的Base有一個虛擬析構函數

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

根據經驗(另見邁耶斯):

如果要通過多態方式刪除基類,則將基類析構函數設為虛擬。

scoped_ptr<>不同, shared_ptr<>顯式記住您傳遞給構造函數的指針類型:

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

醫生說

此構造函數已更改為模板,以便記住傳遞的實際指針類型。 析構函數將使用相同的指針調用 delete,並以其原始類型完成,即使 T 沒有虛擬析構函數或為 void。

這是通過將運行時多態性與靜態多態性混合來實現的。

您需要有一個虛擬析構函數,以便通過指向其基 class 的指針調用派生的 class 析構函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM