使用嵌套的C++类和枚举类型的优缺点是什么?

49

使用嵌套公共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个回答

48

嵌套类

类内部嵌套其他类可能会带来一些副作用,我通常认为这是缺陷(如果不是纯反模式)。

让我们想象以下代码:

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的完整声明的情况下前置声明A::B
  • 可扩展性:除非你是A的所有者,否则不可能添加另一个类A::C
  • 代码冗长:把类放入类只会使头文件更大。你仍然可以将其分成多个声明,但没有使用类似命名空间别名、导入或using的方法。

总之,除非有例外情况(例如嵌套类是嵌套类的一部分...即使如此...),我认为在正常代码中没有嵌套类的必要,因为缺陷比看起来的优点要大得多。

此外,它似乎是一个笨拙的尝试,试图在不使用C++命名空间的情况下模拟命名空间。

在赞成方面,你可以隔离这段代码,如果是私有的,使其只能从"外部"类中使用......

嵌套枚举

优点: 一切。

缺点: 没有。

事实上,枚举项会污染全局作用域:

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

// empty is from Value or Glass?

只有将每个枚举放入不同的命名空间/类中,才能避免此冲突:
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 ;

对于这种类型的问题,正是专门针对此类问题而设计的。


2
嵌套私有类的一个很好的例子是 std::list 中列表中的链接。没有必要让任何人知道它们、使用它们或与它们交互(或者它们是否真的存在;-))。 - Martin York
我偶尔会使用嵌套的私有类来实现pImpl模式。 - xtofl
1
PImpl模式中的嵌套私有类意味着即使它不可访问,用户也将看到实现类。简单的前向声明只会声明名称,而没有其他内容。您可以在其他地方完全定义实现(对用户隐藏)... ^_^ .. - paercebal
1
前向声明问题不仅仅是一个不便之处。你可能会陷入依赖循环的困境,需要解决问题区域的嵌套。解除嵌套可能会导致设计不一致,或者你必须改变整个项目以解除嵌套 : / - Catskul
在与朋友的技术讨论后,我进一步调查后完成了“嵌套类”部分。结果相同(除非有异常情况,否则嵌套类不行),但原因更加详细。 - paercebal

7

对于大型项目来说,一个可能会成为重大问题的缺点是无法为嵌套类或枚举类型进行前向声明。


请注意,使用命名空间来嵌套类使用户能够前向声明此类:即:<code>namespace Foo { class Bar ; }</code>。无论如何,对此评论点赞。 - paercebal

2
使用嵌套公共C++类没有绝对的优缺点。这些事实是由C++标准所规定的。关于嵌套公共C++类的事实是优点还是缺点取决于您要解决的特定问题。您给出的示例不允许判断嵌套类是否适合使用。
有关嵌套类的一个事实是,它们可以特权访问其所属类的所有成员。如果嵌套类不需要该访问权限,则这是一个缺点。但是,如果嵌套类不需要此访问权限,则不应将其声明为嵌套类。有些情况下,类A想要授予某些其他类B特权访问。有三种解决方案:
1. 将B设为A的友元。 2. 将B设为A的嵌套类。 3. 使B需要的方法和属性成为A的公共成员。
在这种情况下,选项3违反了封装,因为A可以控制他的朋友和嵌套类,但不能控制调用他的公共方法或访问他的公共属性的类。
关于嵌套类的另一个事实是,除非您是A的所有者,否则无法将另一个类AC添加为A的嵌套类。然而,这是完全合理的,因为嵌套类具有特权访问。如果可以将AC添加为A的嵌套类,则AC可以欺骗A授予对特权信息的访问;这将违反封装。这与“友元”声明基本相同:友元声明不会授予您任何您的朋友向其他人隐藏的特殊权限;它允许您的朋友访问您对非朋友隐藏的信息。在C ++中,称某人为朋友是一种利他的行为,而不是自我行为。允许类成为嵌套类也是一样。
关于嵌套公共类的其他事实:
- A的作用域是B的符号查找的候选者:如果不希望出现这种情况,请将B设为A的友元,而不是嵌套类。然而,有些情况下确实需要这种类型的符号查找。 - A :: B无法前向声明:A和A :: B紧密耦合。能够在不知道A的情况下使用A :: B只会隐藏这个事实。
总结一下:如果工具不符合您的需求,请不要责怪工具;而是责备自己使用工具的方式;其他人可能有不同的问题,对于这些问题,该工具是完美的。

2
如果你只是用独立类的实现,而不会使用依赖类做任何事情,那么嵌套类是可以的,这是我的观点。
但是当你想将“内部”类作为一个独立的对象使用时,情况可能会变得有些复杂,你需要编写提取器/插入器例程。这种情况并不美好。

2

看起来你应该使用命名空间而不是类来将相关的事物分组。嵌套类的一个缺点是,当你搜索某个部分时,你会得到一个非常大的源文件,这可能很难理解。


1
记住,您始终可以将嵌套类升级为顶级类,但是如果不破坏现有代码,则可能无法执行相反的操作。因此,我的建议是首先将其设置为嵌套类,如果它开始成为问题,请在下一个版本中将其设置为顶级类。

1

paercebal说了我关于嵌套枚举的所有话。

关于嵌套类,我常见的且几乎唯一使用它们的情况是当我有一个类正在操作特定类型的资源,并且我需要一个数据类来表示与该资源特定有关的内容。在你的情况下,output_tray可能是一个很好的例子,但是如果该类将具有任何从包含类外部调用的方法,或者主要不仅是数据类,则通常也不使用嵌套类。我通常也不会嵌套数据类,除非不直接在包含类外引用所包含的类。

因此,例如,如果我有一个printer_manipulator类,它可能会有一个用于打印机操作错误的包含类,但打印机本身则是一个非包含类。

希望这有所帮助。 :)


0

我可以看到嵌套类的缺点,即一个人可能更好地使用通用编程。

如果小类在大类之外定义,您可以使大类成为类模板,并在将来需要时使用任何“小”类与大类。

通用编程是一种强大的工具,而且,在开发可扩展程序时,我们应该牢记它。奇怪的是,没有人提到这一点。


0

对我来说,将其放在外部的一个大问题是它成为全局命名空间的一部分。如果枚举或相关类只适用于它所在的类,那么这是有意义的。因此,在打印机案例中,包括打印机的所有内容都将知道具有完全访问枚举PRINTER_TYPE,而它实际上并不需要知道它。我不能说我曾经使用过内部类,但对于枚举,将其保留在内部似乎更合理。正如另一位发帖者指出的那样,还应该使用命名空间来组织类似的项目,因为堵塞全局命名空间可能真的是一件坏事。我以前曾经参与过庞大的项目,仅在全局命名空间上提供自动完成列表就需要20分钟。在我看来,嵌套的枚举和命名空间类/结构可能是最清晰的方法。


0

我同意那些主张将枚举嵌入类中的帖子,但有些情况下不这样做更有意义(但请至少将其放在命名空间中)。如果多个类正在使用在另一个类中定义的枚举,则这些类直接依赖于拥有该枚举的其他具体类。这肯定代表了设计缺陷,因为该类将负责该枚举以及其他职责。

所以,如果其他代码仅使用该枚举与具体类直接交互,请将枚举嵌入类中。否则,请找到更好的位置来保存枚举,例如命名空间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接