Reputation: 6644
Sorry if this answer is already on this site, but I've looked all over and couldn't find any solutions to my issue. It pertains to same-name functions from inherited classes. Here is my code:
class A
{
public:
int foo(int c) { c = c+1; return c; };
};
class B : public A
{
public:
int foo(int c) { c = c-1; return c; };
};
int main()
{
A array[2];
array[0] = A item1;
array[1] = B item2;
for (int n=0;n<2;n++)
{
cout << array[n].foo(10) << endl;
}
return 0;
}
I would expect an output of:
11 // foo() from A class [10 + 1 = 11]
9 // foo() from B class [10 - 1 = 9 ]
But instead I get
11
11
From testing this out, I have found that the foo() function in the B class does not get called within the for-loop. Instead, the foo() function in the A class is called, even on the B object at array[1].
Is this because I have defined the array as containing objects of the A class only? If so, is there a way I can have the foo() function from the B class be called on the second object within that for-loop?
Thank you in advance for any help!
Upvotes: 6
Views: 11129
Reputation: 26184
You are missing the virtual
keyword.
class A
{
virtual int foo(int c) { c = c+1; return c; };
};
Also, to avoid slicing, instead of array of A
use array of pointers to A
:
A* array[2];
array[0] = new A;
array[1] = new B;
for (size_t i=0; i<2; ++i)
cout << array[i]->foo(10) << endl;
or, better yet, make it a vector of pointers to A (std::vector<A*>
).
Also, as a rule of thumb, destructor of your base class should be virtual too - it's not a problem for this particular example, but if derived classes add new members which they should release when destroyed, making the base class destructor virtual will make sure all the destructors in the hierarchy are called.
Another thing, this:
array[1] = B item2;
will not compile. If you declare the array as an array of pointers to A (or vector of pointers to A), you will be able to do:
array[1] = new B;
Upvotes: 5
Reputation: 110658
I'll forget that array[0] = A item1;
isn't valid C++ and just assume that you're assigning an object of type A
to array[0]
and an object of type B
to array[1]
. Okay, so you have two problems.
The first is known as object slicing. When you copy an object of type B
to an object of type A
, you only copy the A
part of that object. So what you have in array[1]
is not a B
at all, it's just an A
. If you want polymorphism (which you do), then you need to use either pointers or references which provide polymorphic behaviour. That means make your array an A* array[2];
and do array[0] = &item1; array[1] = &item2;
.
Now, when you call a function on a pointer to A
that points to a B
it will still only call A
s foo
member function. Why? Because by default, the function will be looked up on the static type of the object. That static type is A
. If you want to tell the compiler to look up your function on the dynamic type of your object - the true type of your object, which is B
- you need to make that member function virtual. So in A
, do:
virtual int foo(int c) { c = c+1; return c; };
Now when your compiler see that you're calling foo
on an A*
, it'll see that it's virtual and say "Oh okay, I should look up this function dynamically" and it'll find B
's implementation of foo
.
Upvotes: 10
Reputation: 37930
You are assigning your B
instance to a space allocated for an A
type. This leads to "slicing".
So first, you'd have to allow for other types via pointers or references. For example:
A* a = new B;
a->foo(10);
The other thing you must do is alert the compiler that foo()
can be overridden. In C++, declare it as a virtual
function:
virtual int foo(int c) { c = c+1; return c; };
Upvotes: 7