【问题标题】:Polymorphism and shared_ptr member多态性和 shared_ptr 成员
【发布时间】:2014-10-08 16:01:49
【问题描述】:

测试多态性&虚函数&shared_ptr,我试图理解以下最小示例所描述的情况。

class B{
public:
  // Definition of class B
  virtual void someBMethod(){
   // Make a burger
  };
};

class C : public B {
public:
  // Definition of class C
  void someBMethod(){
   // Make a pizza
  };
};

class A{
public:
  A(B& SomeB) : Member(std::make_shared<B>(SomeB)){};
  std::shared_ptr<B> Member;
};

现在,大体上我们可以拥有

int main(){
  C SomeC;
  A SomeA(SomeC);
  A.Member->someBMethod(); // someBMethod from B is being executed.
};

除非我没有从我的实际代码到最小示例中包含一些错误,否则我认为 SomeC 正在被削减为 B,或者至少 someBMethodB 被最后调用线。

问题:什么是初始化Member 的正确方法应该是调用C 中的方法someBMethod

【问题讨论】:

  • Member(std::make_shared&lt;B&gt;(SomeB)) 通过隐式B(const B&amp;) 复制构造函数创建B 的新实例,该实例与原始C 实例无关
  • 谢谢@PiotrS。你知道我的问题的答案吗?即初始化..的正确方法是什么?
  • 你的目标是什么? std::make_shared&lt;B&gt; 明确表示“构建 B 的实例”,而不是“构建一些与 SomeB 完全相同类型的实例,然后将其转换并存储在我的 shared_ptr 中”
  • @PiotrS。我的目标是让A 类拥有一个指向B 的成员,但我可以将B 的任何派生类放在那里并调用它的方法(派生类的方法)。

标签: c++ polymorphism shared-ptr


【解决方案1】:

您正在通过调用std::make_shared&lt;B&gt;(SomeB) 执行slicing 这将构造一个指向B 类型的new 对象的shared_ptr 并使用B 上的复制构造函数构造该对象: B::B(const B&amp; b) 切掉所有关于 SomeB 的 C-ness 的信息。

将 A 更改为:

class A{
public:
  A(const std::shared_ptr<B>& pB) : pMember(pB) {}
  std::shared_ptr<B> pMember;
};

还有主要的:

int main(){
  A SomeA(std::make_shared<C>());
  A.pMember->someBMethod(); // someBMethod from C is being executed.
}

【讨论】:

  • 谢谢。你知道我的问题的答案吗?即初始化..的正确方法是什么?
  • @PiotrS。我称之为切片:将多态对象复制到超类型的新对象。
  • 切片是指通过值将子类的实例传递/分配给超类类型
  • @PiotrS。这是通过使用超类的复制构造函数来完成的 - 你能解释一下这里有什么区别吗?
【解决方案2】:

我认为SomeC 正在被缩减为B

这正是正在发生的事情。 make_shared 创建一个指定类型的新对象,将其参数转发给合适的构造函数。所以这会创建一个新的B,使用它的复制构造函数初始化以复制SomeCB 子对象。

初始化Member 的正确方法应该是什么,以便调用来自C 的方法someBMethod

这很棘手:C 不是共享的,但 Member 是共享的,而且你不能同时拥有它。如果你要求用户传入一个共享指针,这可能是最好的,暴露它要与这个类共享的事实:

A(std::shared_ptr<B> SomeB) : Member(SomeB){}

如果你真的想让它使用非共享对象,你可以创建一个带有虚拟删除器的共享指针,这样它就不会尝试共享所有权:

A(B& SomeB) : Member(std::shared_ptr<B>(&SomeB, [](B*){})){}

但请注意,您现在有责任确保 CA 之后才被销毁,并且它的任何副本都不再需要它。您失去了“拥有”共享指针的安全性。

无论您做什么,都不要简单地从&amp;SomeB 创建一个共享指针。默认删除器会尝试删除它,这是一个错误,因为它不是动态创建的。

【讨论】:

  • 有趣!我还有一个问题,但我认为它太微不足道了,如果我将其作为独立帖子发布,它不会受到好评。我想要的是有一个类A,它有一个像插件一样工作的成员。这样A就可以使用它的动态类型插件的功能了。您能否扩展您的答案以包含一些关于此的想法?
  • 当然,您对A 的构造函数的更改现在避免了对C 进行切片。所以,它正在做我想做的事。我在上面的评论中想问的是,这是否是拥有“插件成员”的常用方式?
  • @Karene:那我想答案是一样的:最好的选择是让Ashared_ptr&lt;Plugin&gt;,并且总是使用智能指针来管理插件。
  • 对不起。我想我找到了一种方法,它看起来更好看。你能帮我判断它是否令人满意吗?思路是:在A的构造函数中,在初始化时,放入A() : Member(&amp;SomeB){};。这样指针Member 就得到了SomeB 的地址,仅此而已。
  • @Karene:不,那会很糟糕——共享指针会尝试删除对象,但不能删除该对象,因为它不是动态分配的。如果您出于某种原因真的非常希望能够使用非动态对象,请按照我的第二个建议为共享指针提供一个虚拟删除器。并且要非常小心对象和指针的生命周期。
猜你喜欢
  • 1970-01-01
  • 2013-03-22
  • 2016-05-06
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多