如何通过多态基类接口使用模板化子类

时间:2016-01-09 09:59:41

标签: c++ templates polymorphism virtual

我有基类Parent类和模板化的Child类。 我想在集合中使用Childs来通过Parent的多态接口枚举它们。 我希望虚拟(多态)函数调用 - 但我只有对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 

预期输出应包含&#34; Child :: print&#34;虚函数调用

4 个答案:

答案 0 :(得分:2)

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

  1. 对象切片。瘸子错了。 std :: vector使用原始对象
  2. 多态性仅适用于指针或参考
  3. 启用动态调度代码应该是这样的:

    #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
    

答案 1 :(得分:1)

原因是Object Slicing
那就是:

  

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

答案 2 :(得分:1)

C ++中的多态性仅适用于引用和指针。 Child是值的向量。它们构造为Parent个实例的副本,并且都是vector<Parent> v = { Parent(myChild), Parent(myDoubleChild) }; 类型的副本。基本上它的作用是:

Parent

如果您要将print作为抽象类(例如,将vector<unique_ptr<Parent>> 更改为纯虚方法),您会看到错误。

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

{{1}}

答案 3 :(得分:0)

如上所述,对象切片是问题所在。使用原始指针向量的替代方法是使用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 ++。