Плюсы и минусы использования вложенных классов C ++ и перечислений? - PullRequest
44 голосов
/ 19 октября 2008

Каковы плюсы и минусы использования вложенных общедоступных классов и перечислений C ++? Например, предположим, что у вас есть класс с именем printer, и этот класс также хранит информацию о выходных лотках, вы можете иметь:

class printer
{
public:
    std::string name_;

    enum TYPE
    {
        TYPE_LOCAL,
        TYPE_NETWORK,
    };

    class output_tray
    {
        ...
    };
    ...
};

printer prn;
printer::TYPE type;
printer::output_tray tray;

В качестве альтернативы:

class printer
{
public:
    std::string name_;
    ...
};

enum PRINTER_TYPE
{
    PRINTER_TYPE_LOCAL,
    PRINTER_TYPE_NETWORK,
};

class output_tray
{
    ...
};

printer prn;
PRINTER_TYPE type;
output_tray tray;

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

Итак, что вы предпочитаете и почему?

Ответы [ 13 ]

44 голосов
/ 19 октября 2008

Вложенные классы

Есть несколько побочных эффектов для классов, вложенных в классы, которые я обычно считаю недостатками (если не чистыми антипаттернами).

Давайте представим следующий код:

class A
{
   public :
      class B { /* etc. */ } ;

   // etc.
} ;

Или даже:

class A
{
   public :
      class B ;

   // etc.
} ;

class A::B
{
   public :

   // etc.
} ;

Итак:

  • Привилегированный доступ: A :: B имеет привилегированный доступ ко всем членам A (методам, переменным, символам и т. Д.), Что ослабляет инкапсуляцию
  • Область A является кандидатом для поиска символов: код изнутри B увидит все символы из A в качестве возможных кандидатов для поиска символов, что может привести к путанице в коде
  • форвард-декларация: Невозможно форвард-декларировать A :: B без полного объявления A
  • Расширяемость: Невозможно добавить другой класс A :: C, если вы не являетесь владельцем A
  • Детализация кода: размещение классов в классах только увеличивает заголовки. Вы все еще можете разделить это на несколько объявлений, но нет никакого способа использовать псевдонимы, импорт или использование, подобные пространству имен.

В заключение, за исключением исключений (например, вложенный класс является неотъемлемой частью класса вложенности ... И даже тогда ...), я не вижу смысла во вложенных классах в нормальном коде, поскольку недостатки перевешивают на величины Воспринимаемые преимущества.

Кроме того, он пахнет неуклюжей попыткой симулировать пространство имен без использования пространств имен C ++.

На стороне поддержки вы изолируете этот код, а если он частный, сделаете его непригодным для использования, но из "внешнего" класса ...

Вложенные перечисления

Плюсы: все.

Con: Ничего.

Дело в том, что перечисленные элементы будут загрязнять глобальный охват:

// collision
enum Value { empty = 7, undefined, defined } ;
enum Glass { empty = 42, half, full } ;

// empty is from Value or Glass?

Ony, поместив каждое перечисление в отдельное пространство имен / класс, позволит вам избежать этого столкновения:

namespace Value { enum type { empty = 7, undefined, defined } ; }
namespace Glass { enum type { empty = 42, half, full } ; }

// Value::type e = Value::empty ;
// Glass::type f = Glass::empty ;

Обратите внимание, что C ++ 0x определил перечисление класса:

enum class Value { empty, undefined, defined } ;
enum class Glass { empty, half, full } ;

// Value e = Value::empty ;
// Glass f = Glass::empty ;

именно для такого рода проблем.

6 голосов
/ 19 октября 2008

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

2 голосов
/ 01 сентября 2013

Нет плюсов и минусов как таковых в использовании вложенных общедоступных классов C ++. Есть только факты. Эти факты предусмотрены стандартом C ++. Является ли факт о вложенных общедоступных классах C ++ преимуществом или недостатком, зависит от конкретной проблемы, которую вы пытаетесь решить. Приведенный вами пример не позволяет судить о том, подходят ли вложенные классы.

Одним из фактов, касающихся вложенных классов, является то, что они имеют привилегированный доступ ко всем членам класса, к которому они принадлежат. Это является недостатком, если вложенные классы не нуждаются в таком доступе. Но если вложенный класс не нуждается в таком доступе, то он не должен был быть объявлен как вложенный класс. Существуют ситуации, когда класс A хочет предоставить привилегированный доступ некоторым другим классам B . Есть три решения этой проблемы

  1. Сделать B другом A
  2. Сделать B вложенным классом A
  3. Создайте методы и атрибуты, которые нужны B , публичные члены A .

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

Еще один факт о вложенных классах заключается в том, что невозможно добавить еще один класс A :: C в качестве вложенного класса A , если вы не являетесь владельцем A . Однако это вполне разумно, поскольку вложенные классы имеют привилегированный доступ. Если бы можно было добавить A :: C в качестве вложенного класса A , то A :: C могло бы обмануть A в предоставление доступа к конфиденциальной информации; и что ты нарушаешь инкапсуляцию. По сути, это то же самое, что и с декларацией friend: декларация friend не предоставляет вам никаких особых привилегий, которую ваш друг скрывает от других; это позволяет вашим друзьям получать доступ к информации, которую вы скрываете от своих друзей. В C ++ называть кого-то другом - это альтруистический акт, а не эгоистический. То же самое относится и к тому, что класс может быть вложенным.

Сом другие факты о вложенных публичных классах:

  • Область A является кандидатом для поиска символов в B : если вы не хотите этого, сделайте B другом A вместо вложенного класса , Однако есть случаи, когда вам нужен именно этот вид поиска символов.
  • A :: B не может быть объявлено заранее : A и A :: B тесно связаны. Возможность использовать A :: B , не зная, A только скрывает этот факт.

Подводя итог: если инструмент не соответствует вашим потребностям, не вините инструмент; винить себя за использование инструмента; у других могут быть другие проблемы, для которых инструмент идеально подходит.

2 голосов
/ 19 октября 2008

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

2 голосов
/ 19 октября 2008

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

Когда вы хотите использовать «внутренний» класс в качестве отдельного объекта, вещи могут стать немного сложными, и вы должны начать писать процедуры извлечения / вставки. Не красивая ситуация.

1 голос
/ 28 марта 2014

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

1 голос
/ 20 октября 2008

paercebal сказал все, что я скажу о вложенных перечислениях.

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

Так, например, если бы у меня был класс printer_manipulator, он мог бы содержать отдельный класс для ошибок манипулирования принтером, но сам принтер был бы автономным классом.

Надеюсь, это поможет. :)

0 голосов
/ 11 сентября 2013

Я вижу «кон» для вложенных классов, что лучше использовать универсальное программирование.

Если маленький класс определен вне большого, вы можете сделать большой класс шаблоном класса и использовать любой «маленький» класс, который вам может понадобиться в будущем с большим классом.

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

0 голосов
/ 31 мая 2013

Единственная проблема с вложенными классами, с которой я столкнулся, заключалась в том, что C ++ не позволяет нам ссылаться на объект включающего класса в функциях вложенных классов. Мы не можем сказать "Включение :: это"

(а может, есть выход?)

0 голосов
/ 20 октября 2008

Visual Studio 2008, похоже, не в состоянии обеспечить intellisense для вложенных классов, поэтому в большинстве случаев я использовал вложенный класс PIMPL. Я всегда помещаю перечисления в класс, если он используется только этим классом, или вне класса в том же пространстве имен, что и класс, когда перечисление используют более одного класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...