【问题标题】:How to use templated child classes by polymorphic base class interface如何通过多态基类接口使用模板化子类
【发布时间】:2016-04-14 00:32:35
【问题描述】:

我有基本的 Parent 类和模板化的 Child 类。 我想使用集合中的 Childs 通过与父级的多态接口来枚举它们。 我希望那里有虚拟(多态)函数调用 - 但我只有对 Parent::print() 的静态类型调用

#include <iostream>
#include <vector>
using namespace std;

class Parent {
public:
    Parent() { cout << " parent ctor "; }
    virtual void print() { cout << " Parent::print "; }
};


template <typename T>
class Child : public Parent {
public:
    Child(T value) : var(value) { cout << " child ctor "; }
    virtual void print() { cout << " Child::print " << var; }
protected:
    T var;
};

int main() {
    Child<int> myChild(1);
    Child<double> myDoubleChild(2.);

    vector<Parent> v = {myChild, myDoubleChild};

    for (auto i : v) {
        i.print();
    }

    return 0;
}

实际输出:

 parent ctor  child ctor  parent ctor  child ctor  Parent::print  Parent::print 

预期输出应包含“Child::print”虚函数调用

【问题讨论】:

    标签: c++ templates polymorphism virtual


    【解决方案1】:

    如上所述,对象切片是问题所在。使用原始指针向量的替代方法是使用 std::reference_wrapper 向量。

    std::reference_wrapper 是一个类模板,它将引用包装在一个可复制、可分配的对象中。它经常被用作将引用存储在通常不能保存引用的标准容器(如 std::vector)中的一种机制

    #include <iostream>
    #include <functional>
    #include <vector>
    using namespace std;
    
    class Parent {
    public:
        Parent() { cout << " parent ctor "; }
        virtual void print() { cout << " Parent::print "; }
    };
    
    
    template <typename T>
    class Child : public Parent {
    public:
        Child(T value) : var(value) { cout << " child ctor "; }
        virtual void print() { cout << " Child::print " << var; }
    protected:
        T var;
    };
    
    int main() {
        Child<int> myChild(1);
        Child<double> myDoubleChild(2.);
    
        typedef std::reference_wrapper<Parent> refType;
    
        vector<refType> v = {refType(myChild), refType(myDoubleChild)};
    
        for (auto i : v) {
            i.get().print();
        }
        return 0;
    }
    

    这段代码的输出是:

    parent ctor  child ctor  parent ctor  child ctor  Child::print 1 Child::print 2
    

    它不是那么干净(仍然需要特殊语法),但可能被认为是更惯用的 C++。

    【讨论】:

      【解决方案2】:

      正如@tchelidze 和@StenSoft 提到的,有两个缺陷:

      1. 对象切片。蹩脚的错误。 std::vector 使用原始对象
      2. 多态性仅适用于指针或引用

      要启用动态调度代码应该是这样的:

      #include <iostream>
      #include <vector>
      using namespace std;
      
      class Parent {
      public:
          Parent() { cout << " parent ctor "; }
          virtual void print() { cout << " Parent::print "; }
      };
      
      
      template <typename T>
      class Child : public Parent {
      public:
          Child(T value) : var(value) { cout << " child ctor "; }
          virtual void print() { cout << " Child::print " << var; }
      protected:
          T var;
      };
      
      int main() {
          Child<int> myChild(1);
          Child<double> myDoubleChild(2.);
      
      //    vector<Parent> v = {myChild, myDoubleChild};
          vector<Parent*> v = {&myChild, &myDoubleChild};
      
          for (auto i : v) {
      //        i.print();
              i->print();
          }
          return 0;
      }
      

      这给出了所需的输出:

      parent ctor  child ctor  parent ctor  child ctor  Child::print 1 Child::print 2
      

      【讨论】:

      • 您可能希望使用智能指针而不是原始指针。
      • @ysdx 没错,这段代码很好用:vector&lt;shared_ptr&lt;Parent&gt;&gt; v = {make_shared&lt;Child&lt;float&gt;&gt;(1), make_shared&lt;Child&lt;int&gt;&gt;(1)};
      • 2.是由 1 引起的,所以是同一个问题
      【解决方案3】:

      C++ 中的多态性仅适用于引用和指针。 vector&lt;Parent&gt; 是一个值向量。这些被构造为Child 实例的副本,并且都是Parent 类型。基本上它的作用是这样的:

      vector<Parent> v = { Parent(myChild), Parent(myDoubleChild) };
      

      如果您将Parent 设为抽象类(例如,将print 更改为纯虚方法),您会看到错误。

      你需要一个指针向量,例如:

      vector<unique_ptr<Parent>>
      

      【讨论】:

        【解决方案4】:

        原因是Object Slicing
        那就是:

        当您将子类的对象分配给超类时。超类对子类中的附加信息一无所知,并且没有空间存储它,因此附加信息被“切掉”

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-31
          • 1970-01-01
          • 2014-06-19
          • 2013-01-11
          • 2019-05-23
          • 2021-05-27
          • 1970-01-01
          相关资源
          最近更新 更多