Phlox Midas
Phlox Midas

Reputation: 4349

Calling a virtual function on a vector of base classes

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

Answers (4)

Yan
Yan

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

masoud
masoud

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;

 

std::reference_wrapper magic!

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.

Live code

Upvotes: 2

David D
David D

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

Jesse Good
Jesse Good

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

Related Questions