【问题标题】:Calling a virtual function on a vector of base classes在基类向量上调用虚函数
【发布时间】:2013-05-01 21:15:48
【问题描述】:

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

#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

在A 1

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

谢谢。

【问题讨论】:

  • 多态需要指针或引用!在m_As.push_back( a );a 引用的对象被复制构造成一个新的A 对象。

标签: c++ virtual


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    快速解决方法是将您的 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&lt;A&gt; m_As; 时,向量只能适合A 对象。如果您改用指针,则多态可以工作并调用正确的printX 函数。但是,如果指向对象的生命周期到期,则存在悬空指针的问题。要处理这个问题,最好使用像std::unique_ptr 这样的智能指针类。

    【讨论】:

      【解决方案3】:

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

       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_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

      【讨论】:

      • std::vector&lt;A&amp;&gt; won't compile. (我有一个答案,其中包括它并在需要指针时将其删除,杰西在这方面击败了我。)
      • @cdhowie:当然,我更新了我的答案并使用基于参考的std::reference_wrapper 编写了一个示例。
      【解决方案4】:
      for ( const auto & a : m_As )
      {
              a.printX();
      }
      

      它将阻止您扩展副本并提供 B 实例而不是 A 实例,以副本的形式出现。

      【讨论】:

        猜你喜欢
        • 2010-09-28
        • 1970-01-01
        • 2012-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-27
        相关资源
        最近更新 更多