Лучший способ достичь поздней стадии полиморфизма - PullRequest
0 голосов
/ 16 мая 2018

У меня есть несколько разрозненных шаблонных чистых абстрактных классов. Я извлекаю из них набор классов, и оттуда я могу использовать их для создания группы объектов. Я хотел бы поместить все эти объекты в контейнер. Тем не менее, они все разных типов. Мне интересно, как осуществить этот полиморфизм поздней стадии.

Скажи, что это мой ранее существующий код, который у меня есть сейчас:

#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_example или B_example, и все, что я изменил в A и B, это то, что я добавил : public Base. Тем не менее, я понятия не имею, как вызывать каждый элемент print*** функцию. Есть ли способ вызвать функции printNum() и printTwiceNum() и сделать их автоматически распознаваемыми?

1 Ответ

0 голосов
/ 16 мая 2018

Самый простой подход - просто создать виртуальную функцию Base::print и сделать так, чтобы ваши производные классы ее реализовали. Но это не всегда уместно.

Другой подход заключается в переходе на dynamic_cast преобразования. Предполагается, что некоторые функции доступны только в некоторых классах. Но это может стать проблематичным, особенно при использовании шаблонов классов, так как вы должны обрабатывать все ожидаемые параметры шаблона.

Чтобы обобщить это, вы можете использовать интерфейсные классы . Допустим, у вас много разных классов, но только небольшое количество вариантов печати. В этом случае может иметь смысл сделать это:

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 { ... };

И теперь, независимо от того, сколько дополнительных классов или расширений шаблонов вам приходится иметь дело, вам нужно обрабатывать только эти интерфейсы:

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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...