shared_ptr和私人继承
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
库时是这样。通常与boost
相似。
还有其他错误,但这会使B*
A*
进行轮回铸造。
为什么会这样?因为构造函数std::shared_ptr<A>
的模板不是你想象的那样!它更像是template <class X> std::shared_ptr(X* v)
.因此,A*
演员的实际B*
被推迟,并且在非朋友成员中失败。
但是如果你投射B*
指针(即 this
) A*
您所在的class B
方法(唯一没有friend
声明的合法地方)。
注意:原则上私人继承没有错。它不是反模式,提供它是有充分理由的。请考虑组合,但禁止应用程序的某些部分"访问"其"真实"类型的对象有很多用途。例如,传递一个对象 A,该对象具有一些只有对象工厂才能访问的 B 螺栓。
PS:构造函数template<class T> shared_ptr<T* v>
的原因是shared_ptr
使用传递给它的类型的删除器。毫无疑问,share_ptr
巧妙地调用"正确"析构函数,即使它不是虚拟的。我的"修复"实际上颠覆了这种聪明,所以要小心传入正确的删除器或(推荐)使A
析构函数虚拟。
.PPS:
最后是一个完全工作的程序(使用STL。对不起,我没有提升):
#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
有一个先决条件,即必须存在一个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,请考虑组合而不是私有继承
- 继承函数的重载解析
- 继承期间显示未知行为的子类
- 头文件-继承c++
- 为什么在保护模式下继承升级不起作用
- 通过继承类使用来自不同命名空间的运算符
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 混合组合和继承的C++问题
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 从类继承时,继承的类是否会通过父类重新定义继承的变量
- 公共与私人继承
- 如何创建从同一类继承的不同对象的向量
- 如何从另一个文件继承私有成员变量和公共函数
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 带有继承的C++工厂
- 我应该避免多重实现继承吗
- C++继承更改成员
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 从具有默认值的部分指定模板类继承时发生SWIG错误,具有不带默认值的正向声明
- 关于C++中具有多重继承"this"指针的说明