![](/img/trans.png)
[英]Why does boost::scoped_ptr prevent PIMPL idiom with BCB6?
[英]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.