shared_ptr和私有继承

inn*_*der 8 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;
}
Run Code Online (Sandbox Code Playgroud)

我明白了

/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’
Run Code Online (Sandbox Code Playgroud)

我的印象是私有继承允许Derived类仍然访问基类的公共接口,但将该接口隐藏到外部客户端.为什么私有继承会导致此错误(如果我公开继承,它会起作用)?

Per*_*xty 5

更改这一行:

prev->setNext(APtr(this)); 
Run Code Online (Sandbox Code Playgroud)

prev->setNext(APtr(static_cast<A*>(this))); 
Run Code Online (Sandbox Code Playgroud)

它编译。

或者至少在使用std库时是这样。它通常类似于boost.

还有其他错误,但这会导致转换B*A*.

为什么这样做?因为构造函数的模板std::shared_ptr<A>不是你想的那样!它更像是template <class X> std::shared_ptr(X* v). 因此,实际B*A*铸被推迟,并在非好友成员失败。

但是,如果您将B*指针(即thisA*强制转换为class B(唯一没有friend声明的合法位置)的方法内部,则您所在的位置。

注意:原则上私有继承没有任何问题。它不是一种反模式,提供它是有充分理由的。请考虑组合,但禁止应用程序的某些部分“访问”其“真实”类型的对象有很多用途。例如,传递一个对象 A,它有一些只有对象工厂可以访问的 B 螺栓。

PS:构造函数的原因template<class T> shared_ptr<T* v>shared_ptr使用传递给它的类型的删除器。正如您毫无疑问知道的那样,share_ptr即使它不是虚拟的,也可以巧妙地调用“正确”的析构函数。我的“修复”实际上颠覆了这种聪明,所以要注意传入正确的删除器或(推荐)使析构函数成为A虚拟的。

缴费灵:

最后是一个完整的程序(使用 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;
}
Run Code Online (Sandbox Code Playgroud)

您需要使用enable_shared_from_this,否则您将尝试创建两个“家庭”,shared_ptr这将不起作用。

我做了一个工厂方法,因为修复构造函数是行不通的!有一个前提是enable_shared_from_this必须存在一个std::shared_ptr,我想这意味着“完全构建”。

以下构造函数对我不起作用:

B(A::APtr prev){
   prev->setNext(shared_from_this());
} 
Run Code Online (Sandbox Code Playgroud)

也就是说,如果您确实继承自,enable_shared_from_this那么将所有构造函数设为私有并提供返回shared_ptr. 否则,如果调用代码本身不能确保“预先存在shared_ptr”的条件,您可能会陷入混乱。如果有的话,这是一件令人讨厌的联轴器。


KAB*_*ult 2

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

在这里,您将boost::shared_ptr指向 B 的指针,就像它是 A 一样。

这就是你的设计中的矛盾。也许声明boost::shared_ptr<A>B 的朋友会有所帮助,但这仍然是一个奇怪的设计。

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

  • @Christophe `shared_ptr&lt;A&gt;` 是一种与 `B` 完全独立的类型。如果“B”受保护地继承自“A”,则“B”的所有子级也将从“A”继承,但像“shared_ptr&lt;A&gt;”这样的单独类型不会知道这一点(除非您将其设为友元) )。 (2认同)