Полиморфизм дизайна, шаблон как абстрактный класс

Как вы проектируете полиморфизм, когда у вас есть член, тип которого зависит от некоторых ограничений.

Скажи, что у меня есть это:

template<typename T>
class Base
{
public:
  Base() = default;
  virtual ~Base() = default;
  T member;
};

class DerivedA : public Base<int>
{
public:
  DerivedA() {member = 5;}
};

class DerivedB : public Base<float>
{
public:
  DerivedB() = default;
};

Я хочу иметь возможность создавать новый производный объект в зависимости от различных параметров, а именно:

Base *b;
if (something)
    b = new DerivedA();
else
    b = new DerivedB();

Очевидно, я не могу этого сделать, так как мне нужно предоставить параметры шаблона для объявления b,

Это плохой дизайн? Как вы справляетесь с этим?

Я мог бы написать небольшую обертку:

class Wrapper() {};

template<typename T>
class Base : public Wrapper
{
// ...
};

Wrapper a, b;
a = new DerivedA;
b = new DerivedB;

Но тогда у меня не будет доступа напрямую к member или другие методы, объявленные в Base или же Derived, Я должен был бы бросить: reinterpret_cast<DerivedA*>(a)->member, делающий полиморфизм бесполезным.

Спасибо

2 ответа

Дизайн обертки должен быть именно тем, что вы ищете. Проблема в том, что C++ имеет статическую типизацию, поэтому вы не можете объявить член, не указав его тип. Обычный способ избежать этого - создать базовый класс, который поддерживает все необходимые функции, и реализовать определенное поведение в производных классах, как вы это сделали.

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

Если это все еще не вариант, рассмотрите способы извлечения вашего члена. Может быть, виртуальные геттеры и сеттеры с соответствующими преобразованиями.

В крайнем случае рассмотрим boost:: option, boost::any. Но на самом деле они реализованы с использованием техники, аналогичной вашей обертке. Таким образом, вы получаете обертку для обертки.

Хорошо, если "доступ" зависит от параметра шаблона T (такое чтение Base.member), то вы должны как- то его поставить. Приведение к одному из производных классов - это один из способов сделать это, но вам не нужно reinterpret_cast, Вам нужно начать использовать указатели / ссылки, чтобы избежать обрезки и позволить корректной работе замещения:

Wrapper *a, *b;
a = new DerivedA;
b = new DerivedB;

int a_member = static_cast<DerivedA*>(a)->member;
float b_member = static_cast<DerivedB*>(b)->member;

И если вы добавите виртуальный метод Wrapper чтобы сделать его полиморфным, вы также можете выполнить динамическое приведение:

DerivedB* b_derived = dynamic_cast<DerivedB*>(b);
if (b_derived != nullptr) {
    float b_member = b_derived->member;
    // ...
}
Другие вопросы по тегам