Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphism and shared_ptr member

Testing polymorphism & virtual functions & shared_ptr, I am trying to understand the situation described by the following minimal example.

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;
};

Now, in the main we can have

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

Unless I didn't include some mistake from my actual code to the minimal example, I think SomeC is getting sliced down to a B, or at least someBMethod from B is being calledin the last line.

Question: What should be the right way to initialize Member in such a way that the method someBMethod from C gets called?

like image 992
Kae Avatar asked Oct 08 '14 16:10

Kae


2 Answers

you're performing slicing by calling std::make_shared<B>(SomeB) This will construct a shared_ptr pointing to a new object of type B and construct that object by using the copy-constructor on B: B::B(const B& b) slicing off all information about the C-ness of SomeB.

change A to:

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

And main:

int main(){
  A SomeA(std::make_shared<C>());
  A.pMember->someBMethod(); // someBMethod from C is being executed.
}
like image 166
BeyelerStudios Avatar answered Oct 11 '22 14:10

BeyelerStudios


I think SomeC is getting sliced down to a B

That's exactly what's happening. make_shared makes a new object of the specified type, forwarding its arguments to a suitable constructor. So this makes a new B, initialised using its copy-constructor to copy the B sub-object of SomeC.

What should be the right way to initialize Member in such a way that the method someBMethod from C gets called?

That's tricky: C is not shared, but Member is, and you can't have it both ways. It's probably best if you require the user to pass in a shared pointer, exposing the fact that it is to be shared with this class:

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

If you really want to allow it to use a non-shared object, you could create a shared pointer with a dummy deleter, so it doesn't try to share ownership:

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

but beware that you are now responsible for ensuring that C isn't destroyed until after A, and any copy of it, no longer requires it. You've lost the safety of an "owning" shared pointer.

Whatever you do, don't simply create a shared pointer from &SomeB. The default deleter will try to delete it, which is an error because it wasn't dynamically created.

like image 34
Mike Seymour Avatar answered Oct 11 '22 14:10

Mike Seymour