Reputation: 4349
I created some code to reproduce the problem:
#include "stdafx.h"
#include <iostream>
#include <vector>
class A
{
protected:
int m_X;
public:
A() {
std::cout << "in A ctor" << std::endl;
m_X = 0;
}
virtual void printX(){ std::cout << "in A " << m_X << std::endl; }
};
class B : public A
{
public:
B() {
std::cout << "in B ctor" << std::endl;
m_X = 1;
}
virtual void printX(){ std::cout << "in B " << m_X << std::endl; }
};
class As
{
public:
void AddA( const A &a ){ m_As.push_back( a ); }
void PrintXs()
{
for ( auto a : m_As )
{
a.printX();
}
}
private:
std::vector<A> m_As;
};
int _tmain(int argc, _TCHAR* argv[])
{
As as;
B b;
as.AddA( b );
as.PrintXs();
system("pause");
return 0;
}
The output of this is:
in A ctor
in B ctor
in A 1
I want "in B 1" instead of "in A 1". I'm sure my understanding of virtual is flawed. How must I change the code to call the B PrintX()? Note that there will be other classes that inherit from A so I really don't want to code a static call.
Thanks.
Upvotes: 2
Views: 3693
Reputation: 1
for ( const auto & a : m_As )
{
a.printX();
}
it will keep you from expanded copy and provide the B-instance instead of A-instance, appeared as copy.
Upvotes: 0
Reputation: 56539
Since you're passing objects by value you can not take advantages of polymorphism. Pass them by (smart) pointers or references.
std::vector<std::shared_ptr<A>> m_As;
// or
std::vector<std::unique_ptr<A>> m_As;
// or
std::vector<A*> m_As; // be careful of bare pointers
// or (since C++11)
std::vector<std::reference_wrapper<A>> m_As;
For the last one, you can use std::reference_wrapper
and std::ref
:
class As
{
public:
void AddA(A &a){ m_As.push_back( std::ref(a) ); }
void PrintXs() const
{
for ( auto a : m_As )
{
a.get().printX();
}
}
private:
std::vector<std::reference_wrapper<A>> m_As;
};
Using last code, you don't have to change main
code.
Upvotes: 2
Reputation: 1591
What you're doing is called slicing. This is where you take an object of a derived class and trim off everything that is not in the parent and assign it to the parent.
What you want to do is use polymorphism to do what you explained. To do this, change your vector from a copy of the object, to a ptr to the object.
If interested in more details, please use the links provided, the information included in them seems to be very complete.
Upvotes: 6
Reputation: 52365
The quick fix is to change your As
class to the following:
class As
{
public:
void AddA( A &a ){ m_As.push_back( &a ); }
void PrintXs()
{
for ( auto a : m_As )
{
a->printX();
}
}
private:
std::vector<A*> m_As;
};
When you use std::vector<A> m_As;
, the vector can only fit A
objects. If you use pointers instead then polymorphism can work and call the correct printX
function. However, this has the problem of dangling pointer if the lifetime of the pointed to object expires. To handle that it would be better to use a smart pointer class like std::unique_ptr
.
Upvotes: 3