2013-08-27 3 views
4

Я видел несколько сообщений по этой проблеме, но не то, что суммирует все варианты, которые у меня есть в моей текущей ситуации, и могут быть решения, которые я не рассматривал.Полиморфные типы возврата в C++

Мое положение довольно общее. У меня есть несколько классов. Inf1Inf2 (которые больше похожи на интерфейсы) имеют чистые виртуальные методы.

Где Inf1 имеет метод

(Inf2& or Inf2* or shared_pointer) foo(/** Some info on how to build the object**/) = 0

(это часть проблемы).

В реализациях Inf1 возвращаются различные реализации Inf2 при реализации foo.

The Inf2 реализациях относительно малы, так что я был бы не против возвращения их стоимости, чтобы они построили копию на результат, но я не могу объявить foo вернуться только Inf2, потому что тогда я бы возвращающий абстрактный объект.

Любые предпочтительные и или творческие способы решения этой проблемы? :)

+0

В чем вы сомневаетесь? Если вы должны использовать Inf2 &, Inf2 * или общий указатель? – bennofs

+10

'std :: unique_ptr ', если вызывающий абонент должен получить право собственности на объект. Действительно, нет (m) каких-либо других вариантов. – Xeo

+0

@ bennofs Точно, какое бы наименее громоздкое, ясное самое красивое, опрятное решение, которое не заставит меня неприятно смотреть в офис (и нет, мы не используем boost: \ so стандартные решения) – SadStudent

ответ

3

У вас есть типы Inf1 и Inf2, которые связаны друг с другом, и по крайней мере один из них содержит абстрактные методы.

Вы хотите вернуть некоторую под-реализацию Inf2, но какой из них определяется во время выполнения.

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

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

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

В-третьих, вы можете использовать что-то вроде boost::variant - a union по набору типов со средствами защиты от доступа к неправильным типам. В то время как boost не может использоваться напрямую, дизайн может быть имитирован. Идея заключается в том, что у вас есть локальное хранилище, в котором вы размещаете новые данные, кроме, может быть, для более крупных объектов, где вместо этого вы используете смарт-указатель. В отличие от второго решения, набор поддерживаемых типов явно указан в вашем типе. Это самое сложное решение (если у вас нет доступа к boost) и требует, чтобы у вас было фиксированное (во время компиляции) множество реализаций Inf2, что все пользователи Inf1 должны иметь полную информацию.

Как уже отмечалось, первое решение является самым простым. Затраты на первое решение основаны только на производительности, и эти результаты производительности не будут трудно исправить после того факта, если вы обнаружите их там, где есть реальные проблемы. Поэтому я бы посоветовал решение №1, а затем профиль, чтобы узнать, слишком ли высокие затраты. Если затраты высоки, перейдите к решению № 3, возможно, в оболочку решения №2.

2

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

Где хранятся объекты подкласса Inf2? И когда и откуда будут удалены реализации Inf2. Ответ на это, как правило, определяет, какую концепцию дескриптора использовать. Например, если вы выделяете реализации Inf2 в пуле памяти, то возврат Inf2* в порядке. Вы также можете использовать unique_ptr<Inf2>, если хотите сделать одно владение, но затем вы не можете скопировать ручку. Вы также можете использовать shared_ptr<Inf2>, если вы хотите скопировать ручки, но вы должны быть осторожны с циклами, и это немного менее эффективно. Вы также можете создать класс-оболочку Inf2Handle, который делает что-то более сложное, например, копирование на запись или другие вещи.

Вы упомянули, что реализации малы, поэтому, возможно, пул памяти является лучшей архитектурой.

Какой из лучших вариантов действительно зависит от того, как следует использовать Inf1 и Inf2.