在基类向量上调用虚函数

时间:2013-05-01 21:15:49

标签: c++ virtual

我创建了一些代码来重现问题:

#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;
}

这个输出是:

在A ctor中

在B ctor

<1> <1>

我希望“在B 1”中而不是“在A 1中”。我确信我对虚拟的理解是有缺陷的。如何更改代码以调用B PrintX()?请注意,将有其他类继承自A,所以我真的不想编写静态调用。

感谢。

4 个答案:

答案 0 :(得分:6)

您正在做的是slicing。这是您获取派生类的对象并修剪不在父级中的所有内容并将其分配给父级的位置。

您要做的是使用polymorphism来执行您所解释的操作。要执行此操作,请将矢量从对象的副本更改为ptr到对象。

如果对更多细节感兴趣,请使用提供的链接,其中包含的信息似乎非常完整。

答案 1 :(得分:3)

快速解决方法是将您的As课程更改为以下内容:

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;
};

使用std::vector<A> m_As;时,向量只能容纳A个对象。如果您使用指针,那么多态可以工作并调用正确的printX函数。但是,如果指向对象的生命周期到期,则存在悬空指针的问题。要处理它,最好使用像std::unique_ptr这样的智能指针类。

答案 2 :(得分:1)

由于您按值传递对象,因此无法利用多态性。通过(智能)指针或引用传递它们。

 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!

对于最后一个,您可以使用std::reference_wrapperstd::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;
};

使用上一个代码,您不必更改main代码。

Live code

答案 3 :(得分:0)

for ( const auto & a : m_As )
{
        a.printX();
}

它会让你远离扩展副本,并提供 B 实例而不是 A 实例,作为副本出现。