[英]shared_ptr and private inheritance
這是一個玩具示例,說明了我遇到的問題。 該應用程序相當無關緊要(它本質上是一個鏈接的元素列表,最后有一個特殊的行為)。 我無法使用派生指針構造基類shared_ptr,並且由於某種原因鏈接到我正在使用私有繼承的事實。
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace std;
// An Item in a linked list
class A
{
public:
//friend class B;
typedef boost::shared_ptr<A> APtr;
A() : next_() {}
A(APtr n) : next_(n) {}
APtr next() { return next_; }
void setNext(APtr n) { next_ = n; }
virtual void doIt() { /* standard behavior */ }
private:
APtr next_;
};
class B : private A // B really is a special A
// that should have different behavior
// at the tail of the chain
// but I want to hide A's interface
// to external clients
{
public:
typedef boost::shared_ptr<B> BPtr;
B(A::APtr prev)
{ // Set this object as the tail
prev->setNext(APtr(this)); /* WHY CAN'T I CONSTRUCT APtr(this)
WITH PRIVATE INH. */
}
void doIt() {/*special behavior at end */}
};
int main()
{
A::APtr dummyPtr;
A::APtr head = boost::make_shared<A>(dummyPtr);
B::BPtr tail = boost::make_shared<B>(head);
for(A::APtr curr = head; curr; curr=curr->next()){
curr->doIt();
}
return 0;
}
我明白了
/usr/include/boost/smart_ptr/shared_ptr.hpp: In constructor ‘boost::shared_ptr<T>::shared_ptr(Y*) [with Y = B, T = A]’:
derived_shared.cpp:31: instantiated from here
/usr/include/boost/smart_ptr/shared_ptr.hpp:352: error: ‘A’ is an inaccessible base of ‘B’
我的印象是私有繼承允許Derived類仍然訪問基類的公共接口,但將該接口隱藏到外部客戶端。 為什么私有繼承會導致此錯誤(如果我公開繼承,它會起作用)?
改變這一行:
prev->setNext(APtr(this));
至
prev->setNext(APtr(static_cast<A*>(this)));
它編譯。
或者至少在使用std
庫時std
。 它通常類似於boost
。
還有其他錯誤,但是將B*
成A*
。
為什么這樣做? 因為構造函數std::shared_ptr<A>
的模板不是你想象的那樣! 它更像是template <class X> std::shared_ptr(X* v)
。 因此,實際的B*
到A*
演員被推遲並且在非朋友成員中失敗。
但是如果你將B*
指針(即this
)轉換為class B
方法中的A*
(唯一沒有friend
聲明合法的地方),你就進入了。
注意:私人繼承原則上沒有任何錯誤。 它不是反模式,並且有充分理由提供。 考慮組合,但禁止應用程序的某些部分“訪問”其“真實”類型的對象有很多用途。 例如,傳遞一個對象A,它有一些只有對象工廠可以訪問的B螺栓。
PS:構造函數是template<class T> shared_ptr<T* v>
是shared_ptr
使用傳遞給它的類型的刪除器。 因為你並不懷疑share_ptr
巧妙地調用'正確'的析構函數,即使它不是虛擬的。 我的“修復”實際上顛覆了這種聰明所以要小心在右側缺失者或(推薦)作出的析構函數傳遞A
虛擬的。
PPS:
最后一個完整的工作程序(使用STL。抱歉,我沒有Boost):
#include <iostream>
#include <memory>
// An Item in a linked list
class A
{
public:
//friend class B;
typedef std::shared_ptr<A> APtr;
A() : next_() {}
A(APtr n) : next_(n) {}
APtr next() { return next_; }
void setNext(APtr n) { next_ = n;}
virtual void doIt() { std::cout<<"normal thing"<<std::endl; }
virtual ~A(){}
private:
APtr next_;
};
class B : public std::enable_shared_from_this<A>, private A // B really is a special A
// that should have different behavior
// at the tail of the chain
// but I want to hide A's interface
// to external clients
{
public:
template<class X> friend class std::enable_shared_from_this;
typedef std::shared_ptr<B> BPtr;
static BPtr makeit(A::APtr prev){
BPtr B(std::make_shared<B>());
prev->setNext(B->shared_from_this());
return B;
}
void doIt() {std::cout<<"end thing"<<std::endl;}
private:
B(){}
};
int main()
{
A::APtr dummyPtr;
A::APtr head = std::make_shared<A>(dummyPtr);
B::BPtr tail = B::makeit(head);
for(A::APtr curr = head; curr; curr=curr->next()){
curr->doIt();
}
return 0;
}
您需要使用enable_shared_from_this
,否則您將嘗試創建shared_ptr
兩個“系列”,這將無效。
我已經制作了一個工廠方法,因為修復構造函數不起作用! enable_shared_from_this
的前提是enable_shared_from_this
存在std::shared_ptr
,我想這意味着'完全構造'。
以下構造函數對我不起作用:
B(A::APtr prev){
prev->setNext(shared_from_this());
}
也就是說,如果你繼承了enable_shared_from_this
那么將所有構造函數設為私有並提供返回shared_ptr
工廠是個好主意。 否則,如果調用代碼本身不能確保“預先存在的shared_ptr
”條件,則可能會遇到麻煩。 一個令人討厭的耦合,如果有的話。
當你使用私有繼承時,你基本上說“我希望B用A來實現,但我不希望它像A(is-a A)一樣使用”
在這里,你給boost::shared_ptr
一個指向B的指針,好像它是A.
這與你的設計相矛盾。 也許聲明boost::shared_ptr<A>
B的朋友會有所幫助,但它仍然是一個奇怪的設計。
附加說明:如果您希望B是一個不暴露A接口的特殊A,請考慮組合而不是私有繼承
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.