2

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

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

这个的输出是:

在演员

在 B ctor

在一个 1

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

谢谢。

4

4 回答 4

6

你正在做的事情叫做slicing。这是您获取派生类的对象并修剪不在父类中的所有内容并将其分配给父类的地方。

你想要做的是使用多态性来做你解释的事情。为此,请将您的向量从对象的副本更改为指向对象的 ptr。

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

于 2013-05-01T21:22:57.060 回答
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.

于 2013-05-01T21:18:41.207 回答
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 魔术!

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

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

实时代码

于 2013-05-01T21:19:16.407 回答
0
for ( const auto & a : m_As )
{
        a.printX();
}

它将阻止您扩展副本并提供 B 实例而不是 A 实例,作为副本出现。

于 2021-01-31T12:21:10.347 回答