2013-12-13 3 views
4

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

class Huey : public IDuck, public ICartoonCharacter 
{ 
... 
}; 

class Dewey : public IDuck, public ICartoonCharacter 
{ 
... 
}; 

class Louie : public IDuck, public ICartoonCharacter 
{ 
... 
}; 

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

Какие у меня варианты? Я мог думать о

  • хранения IDuck * с в моей обертке и dynamic_cast-ИНГ ICartoonCharacter или
  • используя нечто вроде boost::any, делая мою обертку класса-шаблон, с парой static_asserts для обеспечения параметров шаблона наследуется от IDuck и ICartoonCharacter.

но ни один вариант особо апелляций. Есть идеи?

two interfaces, multiple inheritance combine into one container? - родственный вопрос, но ответ Джеймса Канзе не работает для меня, поскольку я не могу изменить 3 класса.

EDIT: Не используйте множественное наследование часто, забыл синтаксис. Теперь наследуем public от обоих интерфейсов.

EDIT: теперь используется dynamic_cast вместо static_cast (который не будет работать).

EDIT: Я нашел ответы Майка Сеймура и Маттиу М, обещающие. Я соглашусь с одним из их ответов, как только я закодировал все это. Благодаря!

+2

Вы не можете static_cast от 'IDuck *' к несвязанной типа '' ICartoonCharacter *. Вы могли бы dynamic_cast при условии, что 'IDuck' имеет хотя бы одну виртуальную функцию. –

+0

Обычно я использую первый вариант, если возможно использование 'dynamic_cast' (см. Комментарий @SteveJessop выше). –

+0

Я не уверен, но я думаю, что даже если ваши классы детей не могут быть изменены, чтобы наследовать от одного родителя (который, в свою очередь, наследуется от обоих текущих родителей), вы все равно можете использовать такой класс (например, «ICartoonDuck».) Вы можете хранить 'ICartoonDuck' в своем контейнере и меньше беспокоиться о проблемах с макетом памяти. (Тем не менее, вам все равно нужны небезопасные роли, но это все равно взлома.) – yzt

ответ

5

Простой вариант для хранения двух указателей в обертке :

struct CartoonDuckWrapper { 
    IDuck * duck; 
    ICartoonCharacter * toon; 

    template <class CartoonDuck> 
    CartoonDuckWrapper(CartoonDuck & cd) : duck(&cd), toon(&cd) {} 
}; 

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

Если базовые классы являются полиморфными (которые, вероятно, являются интерфейсами), вы могли бы сэкономить пространство одного указателя в обмен на затраты времени исполнения, используя dynamic_cast для преобразования одного в другое. static_cast не может использоваться для такого «перекрестного перевода» между базовыми классами.

2

Создать промежуточный класс:

class ILuckyDuck: public IDuck, ICartoonCharacter //... 

с:

class Huey : public ILuckyDuck //... 

и т.д. и магазин:

std::vector<std:shared_ptr<ILuckyDuck>> donald; 
+0

op уже упоминал, что это невозможно для него .. –

+1

Но 'Huey' не является' ILuckyDuck', поэтому это не помогает. –

+1

Но вопроситель конкретно заявил, что он не может изменить класс 'Huey', поэтому это все равно не помогает. –

3

Как и все проблемы в программировании, вы можете решить эту проблему, добавив еще один уровень косвенности.

class ICartoonDuck: public IDuck, public ICartoonCharacter {}; 

template <typename T> 
class CartoonDuck: public ICartoonDuck { 
public: 
    explicit CartoonDuck(T t): _t(std::move(t)) {} 

    // IDuck interface 
    virtual void foo() override { t.foo(); } 

    // ICartoonCharacter interface 
    virtual void bar() override { t.bar(); } 

private: 
    T _t; // or any ownership scheme that makes sense 
}; // class CartoonDuck 

template <typename T> 
CartoonDuck<T> makeCartoonDuck(T t) { return CartoonDuck(std::move(t)); } 

template <typename T, typename... Args> 
std::unique_ptr<CartoonDuck<T>> makeUniqueCartoonDuck(Args&&...) { 
    return std::unique_ptr<CartoonDuck<T>>(new T(std::forward<Args>()...); 
} 

Теперь вы можете успешно хранить std::unique_ptr<ICartoonDuck> в контейнере.

Это может быть использовано как:

std::vector<std::unique_ptr<ICartoonDuck>> cartoonDucks; 
cartoonDucks.push_back(makeUniqueCartoonDuck<Huey>()); 
cartoonDucks.push_back(makeUniqueCartoonDuck<Dewey>()); 
cartoonDucks.push_back(makeUniqueCartoonDuck<Louie>()); 

for (std::unique_ptr<ICartoonDuck> const& cd: cartoonDucks) { 
    cd->foo(); 
    cd->bar(); 
} 
+0

если я правильно понимаю, что «Мультяшная утка» проведет 'Huey'' louie' и т. д., верно? –

+0

@ Koushik: Да, это идея, позвольте мне привести пример, поскольку, по-видимому, это не так очевидно, как я думал. –

+0

Мне нравится это решение, так как оно похоже на idimatic C++. + 1 –

Смежные вопросы