Вопрос

Похоже, у меня было фундаментальное недоразумение о C ++: <

Мне нравится полиморфный контейнерный раствор. Спасибо ТАК, что обратили на это мое внимание :)

<Ч>

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

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

Наконец, учитывая, что я хочу сделать одно или другое, я бы предпочел полиморфизм. Я считаю, что гораздо проще представлять ограничения, такие как & Quot, этот контейнер содержит сопоставимые типы & Quot; - а ля ява.

Подводя меня к теме вопроса: самое абстрактное, я полагаю, что у меня может быть " Container " чистый виртуальный интерфейс, в котором есть что-то вроде " push (void * data) и pop (void * data) " (для записи, я на самом деле не пытаюсь реализовать стек).

Однако мне не очень нравится void * на верхнем уровне, не говоря уже о том, что подпись будет меняться каждый раз, когда я хочу добавить ограничение на тип данных, с которыми может работать конкретный контейнер.

Подводя итог: у нас есть относительно сложные контейнеры, которые имеют различные способы извлечения элементов. Мы хотим иметь возможность варьировать ограничения на элементы, которые могут входить в контейнеры. Элементы должны работать с несколькими типами контейнеров (при условии, что они соответствуют ограничениям данного конкретного контейнера).

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

Итак, должен ли я оставить любовь к интерфейсам типа Java и использовать шаблоны? Должен ли я использовать void * и все статически разыграть? Или я должен пойти с пустым определением класса & Quot; Элемент & Quot; это ничего не объявляет и использует это как мой класс верхнего уровня в " Element " Иерархия?

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

Это было полезно?

Решение

Может быть, у вас нет корневого класса Container, который содержит элементы:

template <typename T>
class Container
{
public: 

   // You'll likely want to use shared_ptr<T> instead.
   virtual void push(T *element) = 0;
   virtual T *pop() = 0;
   virtual void InvokeSomeMethodOnAllItems() = 0;
};

template <typename T>
class List : public Container<T>
{
    iterator begin();
    iterator end();
public:
    virtual void push(T *element) {...}
    virtual T* pop() { ... }
    virtual void InvokeSomeMethodOnAllItems() 
    {
       for(iterator currItem = begin(); currItem != end(); ++currItem)
       {
           T* item = *currItem;
           item->SomeMethod();
       }
    }
};

Эти контейнеры затем можно передавать полиморфно:

class Item
{
public:
   virtual void SomeMethod() = 0;
};

class ConcreteItem
{
public:
    virtual void SomeMethod() 
    {
        // Do something
    }
};  

void AddItemToContainer(Container<Item> &container, Item *item)
{
   container.push(item);
}

...

List<Item> listInstance;
AddItemToContainer(listInstance, new ConcreteItem());
listInstance.InvokeSomeMethodOnAllItems();

Это дает вам интерфейс Контейнера типичным безопасным способом.

Если вы хотите добавить ограничения к типу элементов, которые могут содержаться, вы можете сделать что-то вроде этого:

class Item
{
public:
  virtual void SomeMethod() = 0;
  typedef int CanBeContainedInList;
};

template <typename T>
class List : public Container<T>
{
   typedef typename T::CanBeContainedInList ListGuard;
   // ... as before
};

Другие советы

Вы можете посмотреть на использование стандартного контейнера boost: : любой , если вы храните действительно произвольные данные в контейнере.

Звучит так, как будто бы вы предпочли иметь что-то вроде "> boost :: ptr_container где все, что может храниться в контейнере, должно быть производным от некоторого базового типа, а сам контейнер может давать вам только ссылки на базовый тип.

Простая вещь - определить абстрактный базовый класс с именем Container и создать его подкласс для каждого типа элементов, которые вы можете сохранить. Затем вы можете использовать любой стандартный класс коллекции (std::vector, std::list и т. Д.) Для хранения указателей на <=>. Имейте в виду, что, поскольку вы будете хранить указатели, вам придется обрабатывать их распределение / освобождение.

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

Полиморфизм и шаблоны очень хорошо играют вместе, если вы используете их правильно.

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

Что касается контейнерных интерфейсов: в зависимости от вашего дизайна, возможно, вы также сможете сделать их шаблонными, и тогда у них будут такие методы, как void push(T* new_element). Подумайте, что вы будете знать об объекте, когда захотите добавить его в контейнер (неизвестного типа). Откуда придет объект? Функция, которая возвращает void*? Вы знаете, что это будет сравнимо? По крайней мере, если все классы сохраненных объектов определены в вашем коде, вы можете сделать так, чтобы все они наследовали от общего предка, скажем, Storable, и использовать Storable* вместо void push(Storable* new_element).

Теперь, если вы видите, что объекты всегда будут добавляться в контейнер с помощью метода, подобного <=>, тогда действительно не будет никакой дополнительной ценности в том, чтобы сделать контейнер шаблоном. Но тогда вы узнаете, что в нем должны храниться Storables.

Во-первых, шаблоны и полиморфизм - это ортогональные понятия, и они хорошо сочетаются друг с другом. Далее, почему вы хотите конкретную структуру данных? Как насчет STL или расширенных структур данных (в частности, контейнер для указателей ) у вас не работает.

Учитывая ваш вопрос, похоже, что в вашей ситуации вы неправильно используете наследование. Можно создать & Quot; ограничения . & Quot; на то, что идет в ваших контейнерах, особенно если вы используете шаблоны. Эти ограничения могут выходить за рамки того, что вам даст ваш компилятор и компоновщик. Это на самом деле более неудобно для такого рода вещей с наследованием, и ошибки, скорее всего, остаются на время выполнения.

Используя полиморфизм, вы в основном получаете базовый класс для контейнера и производные классы для типов данных. Базовый класс / производные классы могут иметь столько виртуальных функций, сколько вам нужно, в обоих направлениях.

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

Не забывайте, что вы можете упростить свою жизнь с помощью typedef для каждого из шаблонных типов - особенно если вам позже потребуется превратить один из них в класс.

Вы также можете проверить проверку концепции повышения. Библиотека (BCCL) , которая предназначена для предоставления ограничений на параметры шаблона шаблонных классов, в данном случае, ваших контейнеров.

И просто, чтобы повторить то, что говорили другие, у меня никогда не было проблем со смешиванием полиморфизма и шаблонов, и я сделал с ними довольно сложные вещи.

Вы не можете отказаться от Java-подобных интерфейсов и использовать шаблоны. предложение Джоша о Базовый базовый шаблон Container, безусловно, позволит вам полиморфно передавать контейнеры и их дочерние элементы, но кроме того, вы, безусловно, можете реализовать интерфейсы как абстрактные классы, чтобы они содержали элементы. Нет причины, по которой вы не могли бы создать абстрактный класс IComparable, как вы предлагали, чтобы у вас могла быть полиморфная функция следующим образом:

class Whatever
{
   void MyPolymorphicMethod(Container<IComparable*> &listOfComparables);
}

Теперь этот метод может принимать любого потомка контейнера, содержащего любой класс, реализующий IComparable, поэтому он будет чрезвычайно гибким.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top