Подтвердить что ты не робот

Плюсы и минусы использования вложенных классов С++ и перечислений?

Каковы плюсы и минусы использования вложенных общедоступных классов С++ и перечислений? Например, предположим, что у вас есть класс с именем 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;

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

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

4b9b3361

Ответ 1

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

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

Представьте себе следующий код:

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

   // etc.
} ;

Или даже:

class A
{
   public :
      class B ;

   // etc.
} ;

class A::B
{
   public :

   // etc.
} ;

Итак:

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

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

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

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

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

Плюсы: все.

Кон: Ничего.

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

// 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 ;

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

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

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

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

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

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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

Для меня большой конфликт заключается в том, что он становится частью глобального пространства имен. Если перечисление или связанный класс действительно применимо только к классу, в котором он находится, тогда это имеет смысл. Таким образом, в принтере все, что включает принтер, будет знать о наличии полного доступа к enum PRINTER_TYPE, где это действительно не нужно знать об этом. Я не могу сказать, что я когда-либо использовал внутренний класс, но для перечисления это кажется более логичным, чтобы держать его внутри. Как отметил еще один плакат, также неплохо использовать пространства имен для группировки похожих элементов, так как засорение глобального пространства имен действительно может быть плохой. Я ранее работал над крупными проектами, и только для создания полного полного списка в глобальном пространстве имен требуется 20 минут. По-моему, вложенные enums и namespaced classes/structs, вероятно, являются самым чистым подходом.

Ответ 9

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

Итак, да, вставьте enum в класс, если другой код использует только это перечисление для непосредственного взаимодействия с этим конкретным классом. В противном случае найдите лучшее место для хранения перечисления, такого как пространство имен.

Ответ 10

Если вы поместите enum в класс или пространство имен, intellisense сможет дать вам указания, когда вы пытаетесь запомнить имена перечислений. Маленькая вещь наверняка, но иногда мелочи имеют значение.

Ответ 11

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

Ответ 12

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

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

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

Ответ 13

Только проблема с вложенными классами, с которой я столкнулся, заключалась в том, что С++ не позволяет ссылаться на объект охватывающего класса во вложенных функциях класса. Мы не можем сказать "Enclosing:: this"

(Но может быть, есть способ?)