使用嵌套C++类和枚举的优点和缺点
Pros and cons of using nested C++ classes and enumerations?
使用嵌套的公共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;
我可以看到嵌套私有枚举/类的好处,但当涉及到公共枚举/类时,办公室是分开的——这似乎更像是一种风格选择。
那么,你更喜欢哪一个,为什么?
嵌套类
嵌套在类中的类有几个副作用,我通常认为这些副作用有缺陷(如果不是纯粹的反模式的话)。
让我们想象一下以下代码:
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++名称空间的情况下模拟名称空间,这听起来像是一种笨拙的尝试。
在专业方面,您隔离此代码,如果是私有代码,则使其不可用,但与";外部";班
嵌套枚举
优点:一切。
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 ;
正是针对这类问题。
对于大型项目来说,一个可能成为大问题的缺点是不可能为嵌套类或枚举进行前向声明。
如果你除了使用独立类的实现之外,永远不会使用依赖类,那么在我看来,嵌套类是可以的。
当你想使用"内部"类作为一个对象时,事情可能会变得有点复杂,你必须开始编写提取器/插入器例程。情况不太好。
似乎应该使用名称空间而不是类来对以这种方式相互关联的事物进行分组。我在做嵌套类时看到的一个缺点是,当你搜索一个节时,你最终会得到一个非常大的源文件,这个文件可能很难找到。
使用嵌套的公共C++类本身没有优点和缺点。只有事实。这些事实是由C++标准规定的。关于嵌套公共C++类的事实是赞成还是反对取决于您试图解决的特定问题。您给出的示例不允许判断嵌套类是否合适。
嵌套类的一个事实是,它们对所属类的所有成员都有特权访问。如果嵌套类不需要这样的访问权限,这就是一个骗局。但是,如果嵌套类不需要这样的访问,那么它就不应该被声明为嵌套类。在某些情况下,类a希望授予对某些其他类B的特权访问权限。有三种解决方案
- 让B成为a的朋友
- 使B成为a的嵌套类
- 使B需要的方法和属性成为A的公共成员
在这种情况下,#3违反了封装,因为A可以控制他的朋友和嵌套类,但不能控制调用他的公共方法或访问他的公共属性的类。
关于嵌套类的另一个事实是,除非您是A的所有者,否则不可能将另一个类A::C添加为A::C添加为A的嵌套类,则A::C可以诱使A-授予对特权信息的访问权限;并且违反了封装。这与friend
声明基本相同:friend
声明不授予您任何特权,即您的朋友对他人隐瞒;它允许你的朋友访问你对非朋友隐藏的信息。在C++中,称某人为朋友是一种无私的行为,而不是利己主义行为。允许类成为嵌套类也是如此。
关于嵌套公共类的其他一些事实:
- A的作用域是B符号查找的候选者:如果您不希望这样,请使B成为A的朋友,而不是嵌套类。但是,在某些情况下,您需要的正是这种符号查找
- A::B 不能正向声明:A和A::B紧密耦合。能够在不知道A的情况下使用A::B只会隐藏这一事实
总结一下:如果工具不适合你的需求,不要责怪工具;责怪自己使用了这个工具;其他人可能有不同的问题,对于这些问题,该工具是完美的。
paercebal说了我想说的关于嵌套枚举的一切。
WRT嵌套类,我对它们的常见且几乎唯一的用例是,当我有一个类在操作特定类型的资源时,我需要一个代表该资源特定内容的数据类。在您的情况下,output_tray可能是一个很好的例子,但如果类要有任何从包含类外部调用的方法,或者主要是一个数据类,那么我通常不会使用嵌套类。我通常也不会嵌套数据类,除非所包含的类从未在包含类之外直接引用过。
因此,例如,如果我有一个printer_manifer类,它可能有一个用于打印机操作错误的包含类,但打印机本身将是一个非包含类。
希望这能有所帮助。:)
请记住,您以后总是可以将嵌套类升级为顶级类,但如果不破坏现有代码,您可能无法做到相反的效果。因此,我的建议是首先将其作为嵌套类,如果它开始成为问题,则在下一个版本中将其作为顶级类。
对我来说,将其放在外部的一个大缺点是它成为全局命名空间的一部分。如果枚举或相关类只真正应用于它所在的类,那么它是有意义的。因此,在打印机的情况下,包括打印机在内的所有东西都会知道是否可以完全访问枚举printer_TYPE,而它实际上不需要知道它。我不能说我曾经使用过内部类,但对于枚举来说,将其保留在内部似乎更合乎逻辑。正如另一位发帖者所指出的,使用名称空间对类似项目进行分组也是一个好主意,因为阻塞全局名称空间确实是一件坏事。我以前做过一些大型项目,在全局名称空间中自动列出一个完整的列表需要20分钟。在我看来,嵌套枚举和命名空间类/结构可能是最干净的方法。
我同意那些主张将枚举嵌入类中的帖子,但在某些情况下,不这样做更有意义(但请至少将其放在命名空间中)。如果多个类正在使用不同类中定义的枚举,那么这些类直接依赖于其他具体类(拥有该枚举)。这无疑代表了一个设计缺陷,因为该类将负责该枚举以及其他责任。
所以,是的,如果其他代码只使用该枚举直接与具体类接口,那么就将该枚举嵌入到类中。否则,请找一个更好的地方来保存枚举,例如名称空间。
如果您将枚举放入类或命名空间,intellisense将能够在您尝试记住枚举名称时为您提供指导。这当然是一件小事,但有时小事很重要。
Visual Studio 2008似乎无法为嵌套类提供智能感知,所以在大多数情况下,我都改用了PIMPL习惯用法,因为我以前有一个嵌套类。如果枚举仅由该类使用,我总是将其放在该类中,或者当多个类使用该枚举时,我总是在与该类相同的命名空间中的类外部放置枚举。
我可以看到嵌套类的一个缺点,即可以更好地使用泛型编程。
如果小类是在大类之外定义的,那么您可以将大类作为类模板,并在将来与大类一起使用您可能需要的任何"小"类。
泛型编程是一种强大的工具,IMHO,我们在开发可扩展程序时应该记住它。奇怪的是,没有人提到这一点。
我遇到的嵌套类的唯一问题是C++不允许我们在嵌套类函数中引用封闭类的对象。我们不能说"包围::这个"
(但也许有办法?)
- 不带大括号的枚举形式
- 枚举环境变量的惯用C++14/C++17方法
- 类似枚举的计算常量
- 如何正确实现和访问运算符的各种自定义枚举器
- 错误:从"int"到枚举c++的转换无效
- C++中构造函数中的枚举
- 访问在 C++ 结构中声明的枚举变量
- 枚举类'classname'的多重定义
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- typedef 枚举和枚举类有什么区别?
- 为什么我的开关/机箱在使用枚举时默认?
- 标准::可选枚举的比较运算符
- C++两个源文件之间共享的枚举的静态实例
- 打印没有铸件的枚举可以在C++中吗?
- 枚举成员与静态 int 成员?
- C++:枚举:错误:应使用标识符而不是"}"
- 带有 c++ 的枚举(输入检查)
- 在 qml 中使用 Q_ENUM 和 Q_PROPERTY 作为枚举类
- 为什么 int 类型的枚举类值不能用作 int
- 使用嵌套C++类和枚举的优点和缺点