Calling a virtual function on a vector of base classes

醉酒当歌 提交于 2020-01-04 15:27:11

问题


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.


回答1:


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.




回答2:


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.




回答3:


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



来源:https://stackoverflow.com/questions/16326657/calling-a-virtual-function-on-a-vector-of-base-classes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!