0

Heyo, I'm a little confused about how method overriding works when you involve calling objects as their parent type.

Here is my example code:

#include <iostream>
#include <cstdlib>
#include <vector>

using namespace std;

class A {
public:
    A() {
        std::cout << "Made A.\n";
    }
    void doThing() {
        std::cout << "A did a thing.\n";
    };
};

class B : public A {
public:
    B() {
        std::cout << "Made B.\n";
    }
    void doThing() {
        std::cout << "B did a thing.\n";
    };
};

class C : public A {
public:
    C() {
        std::cout << "Made C.\n";
    }
    void doThing() {
        std::cout << "C did a thing.\n";
    };
};

int main(int argc, char** argv) {
    std::cout << "\n";

    std::cout << "Make objects: \n";
    A a;
    B b;
    C c;

    std::cout << "Call objects normally: \n";
    a.doThing();
    b.doThing();
    c.doThing();

    std::cout << "Call objects as their parent type from a vector: \n";
    vector<A> vect;
    vect.push_back(a); vect.push_back(b); vect.push_back(c);

    for(int i=0;i<vect.size();i++)
        vect.data()[i].doThing();

    return 0;
}

And here is the output I get:

Make objects: 
Made A.
Made A.
Made B.
Made A.
Made C.
Call objects normally: 
A did a thing.
B did a thing.
C did a thing.
Call objects as their parent type from a vector: 
A did a thing.
A did a thing.
A did a thing.

This same code in another language (like Java) would produce this output:

Make objects: 
Made A.
Made B.
Made C.
Call objects normally: 
A did a thing.
B did a thing.
C did a thing.
Call objects as their parent type from a vector: 
A did a thing.
B did a thing.
C did a thing.

In short, how do I achieve that second output in c++?

new Objekt
  • 414
  • 3
  • 8

3 Answers3

1

You need to use the virtual keyword to enable functions to be overwritten in subclasses.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • That's part of it. You also can't get polymorphism with values, so the vector would have to store pointers. – Fred Larson Feb 05 '15 at 16:58
  • I changed my code to virutal void doThing() but it still outputs the same thing. Is there a specific way of using virtual? – new Objekt Feb 05 '15 at 16:59
  • 1
    @newObjekt: See my comment. While Java always uses references in dealing with object types, C++ can use object values. But when you assign a derived class value to a base class value, you lose the derived object information. This is called [slicing](http://stackoverflow.com/q/274626/10077). – Fred Larson Feb 05 '15 at 17:01
1

Whenever you pass a Derived object by value to a function taking a Base, something called "slicing" happens. Basically, only the Base part of the Derived object is being used.

You need to pass the object by reference or by pointer to avoid these issues. For example, declaring

f(Base&)

allows passing in a Derived object, i.e. allows you to write

f(Derived)

In addition, to enable run-time polymorphism, your function must be marked virtual. Java by default has everything implicitly marked virtual. However, this is C++ and you don't pay for what you don't use (virtual functions are an overhead).

PS: in your code, even if you want, you cannot use a std::vector of references. However, you can wrap the objects using std::reference_wrapper which allows you to "simulate" a std::vector of references:

std::vector<std::reference_wrapper<A>> vect

and use the get member function to retrieve the reference

for(int i=0;i<vect.size();i++)
    vect[i].get().doThing();

Or, perhaps simpler, just use a std::vector<A*>

vsoftco
  • 55,410
  • 12
  • 139
  • 252
1

Ok So here is what's happening: Make objects: A is quite obvious I think. The A object gets constructed and its default constructor prints

Made A

When you instantiate a B object first its parent class gets constructed fully. So in this case the parent class is A it gets constructed with the default constructor, which prints out

Made A 

After that the B class remaining part gets constructed and runs its constructor which prints out

Made B

The same thing happens when instantiating C

Calling functions on the objects: Its just a simple function call and since you overwrite the function in each class they get called and not the parent's function.

When you create a vector of the objects you copy the objects into them since you dont pass the reference nor the pointer. You have not written a copy constructor so the default bit by bit copy will run. This way from a B class object you get an A class object which function will print out A did thing and not B did thing. Same happens with C.

Yoghurt
  • 295
  • 3
  • 10