6

I am trying to add a number of subclassed elements into a vector, iterate through them calling a overridden method and want it to call the overridden method where possible. However I have found that it appears to only be calling the superclass method.

I learnt Java and am unsure why it is doing this in C++. I have tried rewriting the code using a vector of pointers of the superclass and casting the pointer of the subclass to the superclass. Accessing this through pointers then works.

Ideally I dont want to have to put a list of pointers into the vector since then I have to manually delete each one (I believe?) to stop memory leaks since I will be creating the objects with new so they persist past the method call to add them into the vector.

Is there a better way to do this or am I stuck to using pointers and calling delete on the created objects when the parent class is unneeded? Preferably the vector would be a list of class X rather than a list of pointers of class X

My structure is:

class a { vector vec of class X,
    method to create and add an instance of X into vector vec,
    method to create and add an instance of Y into vector vec }
class X { talk() }
class Y : public X { talk() }

Code to demonstrate what I ideally want to do, but showing its broken by only calling the superclass method:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>

class A {
  public:
    virtual void talk() { printf("A\n"); }
};

class B: public A {
  public:
      void talk() { printf("B\n"); }
};

int main(void) {
    std::vector<A> vec;
    std::vector<A*> vec2;
    A a;
    B b;
    a.talk();
    b.talk();

    vec.push_back(a);
    vec.push_back(b);
    vec2.push_back(&a);
    vec2.push_back(&b);

    for(int i = 0; i < vec.size(); i++) {
        vec[i].talk();
        vec2[i]->talk(); //bad but short for example
    }

}
Chewett
  • 63
  • 1
  • 6
  • 2
    You do need pointers (possibly smart pointers, like `std::unique_ptr`) in order to obtain polymorphic behavior. See ["object slicing"](http://en.wikipedia.org/wiki/Object_slicing). It works in Java because there, `MyClass obj` effectively declares a pointer to `MyClass`. – Igor Tandetnik Jul 19 '14 at 19:24
  • 1
    `talk()` isn't virtual in `A`, so there is no method to override. The compiler will go with what is the static type, which is `A`. – David G Jul 19 '14 at 19:25
  • @0x499602D2 Mistake in the first version of this code, fixed, added virtual, still not working as expected. – Chewett Jul 19 '14 at 19:32
  • @IgorTandetnik Ah that makes sense, want to add it as an answer? Got any more literature I can read? – Chewett Jul 19 '14 at 19:34

5 Answers5

5

To get the polymorphic behaviour you want you need to add the virtual specifier to the functions in the base class that you want to override in derived classes.

class A {
public:
    virtual void talk() { printf("A\n"); }
};

You should also make a habit of adding the override specifier on overridden functions in derived classes so that the compiler can help you with these kind of issues.

class B: public A {
public:
    virtual void talk() override { printf("B\n"); }
//                      ^ Compiler will report an error if base class' function
//                        is not virtual.
};

Also you can not assign a derived object to an instance of a base class, or slicing will occur.

std::vector<A> vec;
/* ... */
B b;
/* ... */
vec.push_back(b); // Slicing. Information only in B is lost.

Live example using virtual specifier

Live example without virtual specifier

Community
  • 1
  • 1
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • That doesnt work FYI as im still getting AB AA AB from the output. – Chewett Jul 19 '14 at 19:29
  • Originally the code was incorrectly typed, fixed it on my IDE but not here, fixed now. – Chewett Jul 19 '14 at 19:29
  • Might want to remove the first part of your answer as it wasnt an issue (merely copying code was lol). Otherwise looks great, thanks :) – Chewett Jul 19 '14 at 19:36
  • @Chewett The `virtual` specifier is required for polymorphic behaviour. Without it, calling `talk()` will always call `A::talk` regardless if called from an instance of `B`. The `override` specifier is *not* required, but is useful as it tells the compiler what you are trying to accomplish (it might enable optimization though, not sure). – Felix Glas Jul 19 '14 at 19:40
  • I did not think it was required on B since then it means that any more subclasses of B would not be able to override the talk() method of B. something which I wish to happen. This question is purely focusing on overriding the main base class. So do you need the virtual on B in the above case? My compiler says no in that it compiles and produces expected behavior in the pointer case. – Chewett Jul 19 '14 at 19:47
  • 1
    @Chewett The virtual property will automatically be transferred to overriding functions, i.e. if `A::talk` is virtual then `B::talk` will also be virtual regardless if you ommit the `virtual` specifier (it will be secretly virtual). Because of this it is better to specify overriding functions as virtual for clearity. – Felix Glas Jul 19 '14 at 19:51
  • I did not know this, thank you, This means that actually only adding it to one doesnt make sense, since it will be inherited down the line and just makes it unclearer? – Chewett Jul 19 '14 at 19:52
0

You should declare the methods as virtual in order to be able to override them in a subclass. Also it's good practice to make the destructor virtual.

class A {
public:
   virtual void talk() { printf("A\n"); }
   virtual ~A(){}
};

class B: public A {
public:
   // using virtual is not really necessary here, but it's good for clarity.
   virtual void talk() { printf("B\n"); }
};
rashmatash
  • 1,699
  • 13
  • 23
  • That was a mistake in uploading the code, I fixed that before uploading and still have the output AB AA AB – Chewett Jul 19 '14 at 19:27
0

The method should be virtual.
In java methods are virtual by default.

class A {
  public:
    virtual void talk() { printf("A\n"); }
};

class B: public A {
  public:
      virtual void talk() override { printf("B\n"); } //override key word is in C++  0x and above
};
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
  • That was a mistake in uploading the code, I fixed that before uploading and still have the output AB AA AB – Chewett Jul 19 '14 at 19:31
0

I think what you are missing is the virtual keyword in your method declaration. If you want to get to a subclass method when invoking methods in a parent class, the methods have to be declared virtual.

Logicrat
  • 4,438
  • 16
  • 22
  • That was a mistake in uploading the code, I fixed that before uploading and still have the output AB AA AB – Chewett Jul 19 '14 at 19:30
0

If you don't use a pointer you will get 'object slicing' when you copy the object into the vector. This reduces the object to the base type declared in the vector template argument. So there is no subclass, so no subclass method to call, even if the method is virtual.

user207421
  • 305,947
  • 44
  • 307
  • 483