Внутренние определения типов в C ++ - хороший стиль или плохой стиль? - PullRequest
163 голосов
/ 17 апреля 2009

В последнее время я часто сталкиваюсь с тем, что объявляю определения типов, относящиеся к определенному классу внутри этого класса, т.е.

class Lorem
{
    typedef boost::shared_ptr<Lorem> ptr;
    typedef std::vector<Lorem::ptr>  vector;

//
// ...
//
};

Эти типы затем используются в других местах кода:

Lorem::vector lorems;
Lorem::ptr    lorem( new Lorem() );

lorems.push_back( lorem );

Причины, по которым мне это нравится:

  • Уменьшает шум, вносимый шаблонами классов, std::vector<Lorem> становится Lorem::vector и т. Д.
  • Он служит заявлением о намерениях - в приведенном выше примере класс Lorem предназначен для подсчета ссылок через boost::shared_ptr и сохранения в векторе.
  • Это позволяет реализации измениться, т. Е. Если потребуется изменить Lorem для навязчивого подсчета ссылок (через boost::intrusive_ptr) на более поздней стадии, то это будет иметь минимальное влияние на код.
  • Я думаю, что это выглядит «красивее» и, возможно, легче для чтения.

Причины, по которым мне это не нравится:

  • Иногда возникают проблемы с зависимостями - если вы хотите встроить, скажем, Lorem::vector в другой класс, но вам нужно (или нужно) перенаправить объявление Lorem (в отличие от введения зависимости в заголовочный файл), тогда в конечном итоге придется использовать явные типы (например, boost::shared_ptr<Lorem> вместо Lorem::ptr), что немного противоречиво.
  • Это может быть не очень распространено, и, следовательно, труднее понять?

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

Ответы [ 9 ]

139 голосов
/ 17 апреля 2009

Я думаю, что это отличный стиль, и я использую его сам. Всегда лучше максимально ограничить область имен, и использование классов - лучший способ сделать это в C ++. Например, стандартная библиотека C ++ интенсивно использует typedefs внутри классов.

13 голосов
/ 05 января 2010

Он служит заявлением о намерениях - в приведенном выше примере класс Lorem предназначен для подсчета ссылок через boost :: shared_ptr и хранится в вектор.

Это именно то, что он делает не делает.

Если я вижу 'Foo :: Ptr' в коде, я абсолютно не знаю, является ли он shared_ptr или Foo * (STL имеет :: pointer typedefs типа T *, помните) или что-то еще. Esp. если это разделяемый указатель, я вообще не предоставляю typedef, но оставляю явное использование shared_ptr в коде.

На самом деле я почти никогда не использую typedefs вне шаблонного метапрограммирования.

STL делает подобные вещи постоянно

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

Не используйте STL в качестве причины для совершения таких же ошибок.

9 голосов
/ 17 апреля 2009

Typedefs - это те, на которых основанный на политике дизайн и черты построены в C ++, поэтому мощь универсального программирования в C ++ проистекает из самих typedef.

8 голосов
/ 17 апреля 2009

Typdefs, безусловно, хороший стиль. И все твои «причины, которые мне нравятся», правильные и правильные.

О проблемах, которые у вас есть с этим. Что ж, предварительная декларация не является святым Граалем. Вы можете просто разработать свой код, чтобы избежать многоуровневых зависимостей.

Вы можете переместить typedef за пределы класса, но Class :: ptr настолько красивее, чем ClassPtr, что я этого не делаю. Как и в случае с пространствами имен, для меня все остается в пределах видимости.

Иногда я делал

Trait<Loren>::ptr
Trait<Loren>::collection
Trait<Loren>::map

И это может быть по умолчанию для всех классов домена и с некоторой специализацией для определенных.

3 голосов
/ 17 апреля 2009

Еще один голос за то, что это хорошая идея. Я начал это делать при написании симуляции, которая должна была быть эффективной как во времени, так и в пространстве. Все типы значений имеют Ptr typedef, который начинался как общий указатель boost. Затем я провел некоторое профилирование и изменил некоторые из них на интрузивный указатель без необходимости изменения кода, в котором использовались эти объекты.

Обратите внимание, что это работает только тогда, когда вы знаете, где будут использоваться классы, и что все виды применения имеют одинаковые требования. Я бы не использовал это в коде библиотеки, например, потому что вы не можете знать при написании библиотеки контекст, в котором она будет использоваться.

3 голосов
/ 17 апреля 2009

STL все время делает подобные вещи - определения типов являются частью интерфейса для многих классов в STL.

reference
iterator
size_type
value_type
etc...

- это все определения типов, которые являются частью интерфейса для различных классов шаблонов STL.

2 голосов
/ 18 января 2014

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

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

Поэтому, прежде чем их использовать, нужно учесть некоторые моменты

  • Кому-нибудь еще нужны эти typedefs? Используется ли класс другими классами?
  • Сократить ли использование или скрыть класс? (В случае сокрытия вы также можете подумать об интерфейсах.)
  • Работают ли другие люди с кодом? Как они это делают? Будут ли они думать, что это легче, или они запутаются?
1 голос
/ 17 апреля 2009

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

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

0 голосов
/ 17 апреля 2009

Когда typedef используется только внутри самого класса (то есть объявляется как private), я думаю, это хорошая идея. Однако именно по указанным вами причинам я бы не стал использовать его, если typedef нужно знать за пределами класса. В этом случае я рекомендую вывести их за пределы класса.

...