为什么要在 C++ 中使用嵌套类?
有人可以向我指出一些用于理解和使用嵌套类的好资源吗?我有一些诸如编程原则之类的材料 IBM 知识中心 - 嵌套类
但我仍然无法理解它们的用途。有人可以帮我吗?
Can someone please point me towards some nice resources for understanding and using nested classes? I have some material like Programming Principles and things like this IBM Knowledge Center - Nested Classes
But I'm still having trouble understanding their purpose. Could someone please help me?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
嵌套类对于隐藏实现细节来说很酷。
列表:
在这里,我不想公开 Node,因为其他人可能决定使用该类,这会妨碍我更新我的类,因为公开的任何内容都是公共 API 的一部分,必须永远维护。通过将类设为私有,我不仅隐藏了实现,我还说这是我的,我可以随时更改它,这样您就无法使用它。
看看
std::list
或std::map
它们都包含隐藏类(或者确实如此?)。关键是它们可能会也可能不会,但由于实现是私有的和隐藏的,STL 的构建者能够更新代码,而不会影响您使用代码的方式,或者在 STL 周围留下大量旧包袱,因为他们需要为了与一些决定使用隐藏在list
中的 Node 类的傻瓜保持向后兼容性。Nested classes are cool for hiding implementation details.
List:
Here I don't want to expose Node as other people may decide to use the class and that would hinder me from updating my class as anything exposed is part of the public API and must be maintained forever. By making the class private, I not only hide the implementation I am also saying this is mine and I may change it at any time so you can not use it.
Look at
std::list
orstd::map
they all contain hidden classes (or do they?). The point is they may or may not, but because the implementation is private and hidden the builders of the STL were able to update the code without affecting how you used the code, or leaving a lot of old baggage laying around the STL because they need to maintain backwards compatibility with some fool who decided they wanted to use the Node class that was hidden insidelist
.嵌套类就像常规类一样,但是:
一些示例:
公开嵌套类以将其放入相关类的范围中
假设您希望有一个类
SomeSpecificCollection
来聚合类Element
的对象。然后,您可以:声明两个类:
SomeSpecificCollection
和Element
- 不好,因为名称“Element”足够通用,可能会导致名称冲突< /p>引入命名空间
someSpecificCollection
并声明类someSpecificCollection::Collection
和someSpecificCollection::Element
。没有名称冲突的风险,但它可以变得更详细吗?声明两个全局类
SomeSpecificCollection
和SomeSpecificCollectionElement
- 这有一些小缺点,但可能没问题。声明全局类
SomeSpecificCollection
和类Element
作为其嵌套类。然后:SomeSpecificCollection
的实现中,您仅引用Element
,而在其他任何地方都引用SomeSpecificCollection::Element
- 看起来 +- 与3.但更清晰SomeSpecificCollection
也是一个类。在我看来,最后一个变体绝对是最直观的,因此也是最好的设计。
让我强调一下 - 这与使用更详细的名称创建两个全局类没有太大区别。这只是一个很小的细节,但恕我直言,它使代码更加清晰。
在类作用域内引入另一个作用域
这对于引入 typedef 或枚举特别有用。我将在这里发布一个代码示例:
然后将调用:
但是在查看
Product::
的代码完成建议时,人们通常会获得所有可能的枚举值(BOX、FANCY、CRATE)列出来,这里很容易犯错误(C++0x 的强类型枚举可以解决这个问题,但没关系)。但是,如果您使用嵌套类为这些枚举引入附加范围,则情况可能如下所示:
然后调用如下所示:
然后通过在 IDE 中键入
Product::ProductType::
,人们将仅获得枚举从建议的所需范围。这也降低了犯错误的风险。当然,对于小类来说,这可能不需要,但是如果一个类有很多枚举,那么对于客户端程序员来说,这会让事情变得更容易。
同样,如果需要的话,您可以在模板中“组织”大量 typedef。有时这是一个有用的模式。
PIMPL 习惯用法
PIMPL(Pointer to IMPLementation 的缩写)是一种习惯用法,可用于从标头中删除类的实现细节。当头的“实现”部分发生变化时,这减少了根据类头重新编译类的需要。
它通常使用嵌套类来实现:
Xh:
X.cpp:
如果完整的类定义需要来自某些外部库的类型定义,而该外部库具有繁重或丑陋的头文件(以 WinAPI 为例),则这特别有用。如果您使用 PIMPL,则只能将任何特定于 WinAPI 的功能包含在
.cpp
中,而永远不要将其包含在.h
中。Nested classes are just like regular classes, but:
Some examples:
Publicly nesting class to put it in a scope of relevant class
Assume you want to have a class
SomeSpecificCollection
which would aggregate objects of classElement
. You can then either:declare two classes:
SomeSpecificCollection
andElement
- bad, because the name "Element" is general enough in order to cause a possible name clashintroduce a namespace
someSpecificCollection
and declare classessomeSpecificCollection::Collection
andsomeSpecificCollection::Element
. No risk of name clash, but can it get any more verbose?declare two global classes
SomeSpecificCollection
andSomeSpecificCollectionElement
- which has minor drawbacks, but is probably OK.declare global class
SomeSpecificCollection
and classElement
as its nested class. Then:SomeSpecificCollection
you refer to justElement
, and everywhere else asSomeSpecificCollection::Element
- which looks +- the same as 3., but more clearSomeSpecificCollection
is also a class.In my opinion, the last variant is definitely the most intuitive and hence best design.
Let me stress - It's not a big difference from making two global classes with more verbose names. It just a tiny little detail, but imho it makes the code more clear.
Introducing another scope inside a class scope
This is especially useful for introducing typedefs or enums. I'll just post a code example here:
One then will call:
But when looking at code completion proposals for
Product::
, one will often get all the possible enum values (BOX, FANCY, CRATE) listed and it's easy to make a mistake here (C++0x's strongly typed enums kind of solve that, but never mind).But if you introduce additional scope for those enums using nested classes, things could look like:
Then the call looks like:
Then by typing
Product::ProductType::
in an IDE, one will get only the enums from the desired scope suggested. This also reduces the risk of making a mistake.Of course this may not be needed for small classes, but if one has a lot of enums, then it makes things easier for the client programmers.
In the same way, you could "organise" a big bunch of typedefs in a template, if you ever had the need to. It's a useful pattern sometimes.
The PIMPL idiom
The PIMPL (short for Pointer to IMPLementation) is an idiom useful to remove the implementation details of a class from the header. This reduces the need of recompiling classes depending on the class' header whenever the "implementation" part of the header changes.
It's usually implemented using a nested class:
X.h:
X.cpp:
This is particularly useful if the full class definition needs the definition of types from some external library which has a heavy or just ugly header file (take WinAPI). If you use PIMPL, then you can enclose any WinAPI-specific functionality only in
.cpp
and never include it in.h
.我不太使用嵌套类,但我偶尔会使用它们。特别是当我定义某种数据类型,然后我想定义一个专为该数据类型设计的 STL 仿函数时。
例如,考虑一个具有 ID 号、类型代码和字段名称的通用
Field
类。如果我想通过 ID 号或名称搜索这些Field
的向量
,我可能会构造一个仿函数来执行此操作:然后需要搜索这些
Field
的代码code>Field 可以使用Field
类本身范围内的match
:I don't use nested classes much, but I do use them now and then. Especially when I define some kind of data type, and I then want to define a STL functor designed for that data type.
For example, consider a generic
Field
class that has an ID number, a type code and a field name. If I want to search avector
of theseField
s by either ID number or name, I might construct a functor to do so:Then code that needs to search for these
Field
s can use thematch
scoped within theField
class itself:可以使用嵌套类实现 Builder 模式。特别是在 C++ 中,我个人发现它在语义上更清晰。例如:
而不是:
One can implement a Builder pattern with nested class. Especially in C++, personally I find it semantically cleaner. For example:
Rather than:
我认为使一个类成为嵌套类而不仅仅是一个友元类的主要目的是能够在派生类中继承嵌套类。 C++ 中的友谊不是继承的。
I think the main purpose of making a class to be nested instead of being just a friend class is the ability to inherit nested class within derived one. Friendship is not inherited in C++.
您还可以考虑主要功能的一流类型,您可以在其中启动所有需要的类来一起工作。例如,像游戏类一样,启动所有其他类,如窗口、英雄、敌人、关卡等。这样你就可以从 main 函数中删除所有这些东西。您可以在其中创建游戏对象,并且可能执行一些与 Gemente 本身无关的额外外部调用。
You also can think about first class ass type of main function, where You initiate all needed classes to work togheter. Like for example class Game, initiate all other classes like windows, heroes, enemy's, levels and so on. This way You can get rid all that stuff from main function it self. Where You can create obiect of Game, and maybe do some extra external call not related to Gemente it self.