实现后期多态性的最佳方式

Best way to achieve late-stage polymorphism

我有几个不同的模板化纯抽象类。我从这些派生得到一堆 类,从那里,我可以用它们来制作一堆对象。我想将所有这些对象放入一个容器中。但是,它们都是不同的类型。我想知道如何完成这个后期多态。

假设这是我现在已有的代码:

#include <iostream>

template<typename T>
class A{
public: 
    A() : m_num(1.0) {};
    virtual ~A() {};
    virtual void printNum() const = 0;
protected:
    T m_num;
};

template<typename T>
class B{
public: 
    B() : m_num(2.0) {};
    virtual ~B() {};
    virtual void printTwiceNum() const = 0;
protected:
    T m_num;
};

class A_example : public A<int>
{
public:
    A_example() : A<int>() {};
    void printNum() const { std::cout << m_num << "\n"; };
};


class B_example : public B<int>
{
public:
    B_example() : B<int>() {};
    void printTwiceNum() const { std::cout << 2*m_num << "\n"; };
};

int main(){
    A_example first;
    B_example second;
    first.printNum();
    second.printTwiceNum();

    return 0;
}

随着 类 的增加,main() 内部可能会变得非常混乱。理想情况下,我可以直接遍历容器并在每个元素上调用 print()。我的第一个想法是使用 std::vector<unique_ptr<Base>>。这似乎有效:

#include <iostream>
#include <vector> // new include
#include <memory> // new include
#include <utility> // new include

// new Base class here
class Base{
public: 
    virtual ~Base(){};
};

template<typename T>
class A : public Base{ // new inheritance here
public: 
    A() : m_num(1.0) {};
    virtual ~A() {};
    virtual void printNum() const = 0;
protected:
    T m_num;
};

template<typename T>
class B : public Base{ // new inheritance here as well
public: 
    B() : m_num(2.0) {};
    virtual ~B() {};
    virtual void printTwiceNum() const = 0;
protected:
    T m_num;
};

class A_example : public A<int>
{
public:
    A_example() : A<int>() {};
    void printNum() const { std::cout << m_num << "\n"; };
};


class B_example : public B<int>
{
public:
    B_example() : B<int>() {};
    void printTwiceNum() const { std::cout << 2*m_num << "\n"; };
};


int main(){

    std::vector<std::unique_ptr<Base>> v;
    v.emplace_back( new A_example() );
    v.emplace_back( new B_example() );
    //v[0]->printNum(); // nope
    //v[1]->printTwiceNum(); // nope

    return 0;
}

这很酷,因为我不必更改 A_exampleB_example,我在 AB 中所做的更改只是添加了 : public Base。但是,我不知道如何调用每个元素 print*** 函数。有没有办法调用 printNum()printTwiceNum() 函数,并自动识别它们?

最简单的方法是创建一个虚函数 Base::print 并让派生的 classes 实现它。但这并不总是合适的。

另一种方法是对 dynamic_cast 转换进行分支。前提是有些功能只能在某些 classes 上使用。但这可能会变得棘手,尤其是在使用 class 模板时,因为您必须处理所有预期的模板参数。

要概括这一点,您可以使用 接口 classes。假设您有很多不同的 classes 但只有少量印刷变体。在那种情况下,这样做可能有意义:

class PrintNumInterface {
public:
    virtual void printNum() const = 0;
};

class PrintTwiceNumInterface {
public:
    virtual void printTwiceNum() const = 0;
};

template<typename T> class A : public Base, public PrintNumInterface { ... };
template<typename T> class B : public Base, public PrintTwiceNumInterface { ... };

而现在,无论您要处理多少额外的classes或模板扩展,您只需要处理这些接口:

for (auto& p : v)
{
    if (PrintNumInterface* iface = dynamic_cast<PrintNumInterface*>(p.get())
        iface->printNum();
    else if (PrintTwiceNumInterface* iface = dynamic_cast<PrintTwiceNumInterface*>(p.get())
        iface->printTwiceNum();
}