0

I have these two classes:

class A
{
public:
    A();
    virtual ~A();

    virtual void TellMyName();
};

class B : public A
{
private:
    std::string szName;
public:
    B();
    ~B();

    void TellMyName();

    void SetName(std::string val){ szName = val; }
};

And this is my code:

void main()
{
    std::vector<A*> List_A;

    B* B_Instance = new B();
    B_Instance->SetName("B");

    List_A.push_back(B_Instance); // Way 1
    List_A.push_back(new A((*B_Instance))); // Way 2

    List_A[0]->TellMyName();
    List_A[1]->TellMyName();
}

TellMyName() is just going to prompt a message box. If I use "Way 1" there is no problem with it, but if I use "Way 2" it would prompt the message with no text and that means all members of the B class are empty like they're never being filled with anything. I solved this with the use of std::shared_ptr, but is there any other way to not use smart pointers because I have to implement this approach in a big project and there would be a lot of change and failure. And by the way, what's the cause to "Way 2"?

MahanGM
  • 2,352
  • 5
  • 32
  • 45

4 Answers4

6
List_A.push_back(new A((*B_Instance))); // Way 2

This line is not doing what you think it is.

You are creating a new instance of the class "A" on the heap by calling the default copy constructor generated by C++. That copy constructor uses the instance of B (treated as though it were an instance of A) and does a field-by-field copy, which in this case, does nothing.

You're essentially creating an empty instance of A and pushing it onto the list.

As many have mentioned, the formal term for this is called object slicing.

Community
  • 1
  • 1
riwalk
  • 14,033
  • 6
  • 51
  • 68
2

that means all members of the B class are empty like they're never being filled with anything

No. it doesn't mean they're empty. To be empty, they'd have to be there in the first place.

List_A[1]->TellMyName(); performs dynamic dispatch and ends up calling A::TellMyName, because class A is the actual type of the object. Members of the B class don't even exist.

You've run into the slicing problem.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2
std::vector<A*> List_A;
// ...
List_A.push_back(new A((*B_Instance))); // Way 2

You are suffering from a form of Object Slicing, sort of.

Object slicing happens when you instantiate (or assign) a base class with the values from a derived type, and lose -- or "slice off" -- all the stuff that was unique to the derived class.

Above, you are instantiating a new A, rather than a new B. But since B is derived from A, you can simply instantiate a new B and cast the returned pointer to an A*:

List_A.push_back(new B(*B_Instance)); // Way 2

All of this being said, as an aside I would strongly advise you to consider refactoring your code to use smart pointers.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
2

You should be calling

List_A.push_back(new B((*B_Instance)));

As B inherits from A it can also be added to the list, but as the copy constructor of B will also copy all fields in B, it will work.

Calling A's copy constructor doesn't copy the fields in B, as A doesn't "know" about B's fields.

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139