shared_ptr和私有继承

时间:2015-06-10 18:22:05

标签: c++ inheritance shared-ptr

这是一个玩具示例,说明了我遇到的问题。该应用程序相当无关紧要(它本质上是一个链接的元素列表,最后有一个特殊的行为)。我无法使用派生指针构造基类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类仍然访问基类的公共接口,但将该接口隐藏到外部客户端。为什么私有继承会导致此错误(如果我公开继承,它会起作用)?

2 个答案:

答案 0 :(得分:4)

更改此单行:

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)。因此,实际的B*A*演员阵容被推迟并且在非朋友成员中失败。

但是如果你将B*指针(即this)投放到A*的{​​{1}}方法中(唯一没有class B合法的地方你宣布进入。

NB:私有继承原则上没有任何错误。它不是反模式,并且有充分理由提供。考虑组合,但考虑禁止应用程序的某些部分访问&#39;他们的真实&#39;类型有很多用途。例如,传递一个对象A,它有一些只有对象工厂可以访问的B螺栓。

PS:构造函数为friend的原因是template<class T> shared_ptr<T* v>使用传递给它的类型的删除器。 因为您并不怀疑shared_ptr聪明地称之为“正确”。析构函数,即使它不是虚拟的。 我的修复&#39;实际上颠覆了聪明,所以要小心传递正确的删除器或(推荐)使析构函数share_ptr虚拟。

PPS:

最后一个完整的工作程序(使用STL。抱歉,我没有Boost):

A

您需要使用#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; } ,否则您将尝试创建两个家庭&#39; 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&#39;条件。如果有的话,这是一个讨厌的耦合。

答案 1 :(得分:2)

当你使用私有继承时,你基本上是说“我希望B用A来实现,但我不希望它像A(is-a A)一样使用”

在这里,你给boost::shared_ptr指向B的指针,好像它是A。

这与您的设计相矛盾。也许宣布B的朋友boost::shared_ptr<A>会有所帮助,但这仍然是一个奇怪的设计。

附加说明:如果您希望B成为不暴露A接口的特殊A,请考虑组合而不是私有继承