有人可以指点我一些很好的资源来理解和使用嵌套类吗?我有一些像编程原则和类似IBM Knowledge Center - Nested Classes
之类的材料但我仍然无法理解他们的目的。有人可以帮助我吗?
答案 0 :(得分:206)
嵌套类很容易隐藏实现细节。
列表:
class List
{
public:
List(): head(nullptr), tail(nullptr) {}
private:
class Node
{
public:
int data;
Node* next;
Node* prev;
};
private:
Node* head;
Node* tail;
};
这里我不想公开Node,因为其他人可能决定使用该类,这会阻碍我更新我的类,因为任何公开的API都是公共API的一部分,必须保持永远。通过使类私有,我不仅隐藏了实现,我也说这是我的,我可以随时更改它,所以你不能使用它。
查看std::list
或std::map
它们都包含隐藏的类(或者它们是什么?)。重点是它们可能会也可能不会,但是因为实现是私有的并且隐藏了STL的构建者能够在不影响您使用代码的方式的情况下更新代码,或者因为需要而在STL周围留下大量旧行李保持与某些傻瓜的向后兼容性,他们决定使用隐藏在list
内的Node类。
答案 1 :(得分:136)
嵌套类就像常规类一样,但是:
一些例子:
假设您希望有一个类SomeSpecificCollection
来聚合类Element
的对象。然后你可以:
声明两个类:SomeSpecificCollection
和Element
- 不好,因为名称“Element”足够通用以引起可能的名称冲突
引入名称空间someSpecificCollection
并声明类someSpecificCollection::Collection
和someSpecificCollection::Element
。没有名字冲突的风险,但是它会变得更加冗长吗?
声明两个全局类SomeSpecificCollection
和SomeSpecificCollectionElement
- 这有一些小缺点,但可能没问题。
将全局类SomeSpecificCollection
和类Element
声明为其嵌套类。然后:
SomeSpecificCollection
的实施中,您只引用Element
,其他地方仅引用SomeSpecificCollection::Element
- 其外观+ - 与3.相同,但更清晰SomeSpecificCollection
也是一个类。在我看来,最后一个版本绝对是最直观,最好的设计。
让我强调一下 - 与制作两个名字更详细的全局类并没有太大区别。它只是一个小细节,但它使代码更清晰。
这对于引入typedef或枚举特别有用。我将在这里发布一个代码示例:
class Product {
public:
enum ProductType {
FANCY, AWESOME, USEFUL
};
enum ProductBoxType {
BOX, BAG, CRATE
};
Product(ProductType t, ProductBoxType b, String name);
// the rest of the class: fields, methods
};
然后会打电话给:
Product p(Product::FANCY, Product::BOX);
但是在查看Product::
的代码完成提议时,通常会列出所有可能的枚举值(BOX,FANCY,CRATE)并且这里很容易出错(C ++ 0x的强类型枚举)有点解决,但没关系)。
但是如果你为使用嵌套类的枚举引入额外的范围,事情可能如下:
class Product {
public:
struct ProductType {
enum Enum { FANCY, AWESOME, USEFUL };
};
struct ProductBoxType {
enum Enum { BOX, BAG, CRATE };
};
Product(ProductType::Enum t, ProductBoxType::Enum b, String name);
// the rest of the class: fields, methods
};
然后电话看起来像:
Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);
然后在IDE中键入Product::ProductType::
,只会得到建议所需范围的枚举。这也降低了犯错的风险。
当然,小类可能不需要这样,但如果有很多枚举,那么它会让客户端程序员更容易。
同样,如果您有需要,可以在模板中“组织”一大堆typedef。这有时是一种有用的模式。
PIMPL(指向IMPLementation的指针的缩写)是一个习惯用法,用于从标题中删除类的实现细节。每当标题的“实现”部分发生变化时,这就减少了重新编译类的需要,具体取决于类的标题。
它通常使用嵌套类实现:
X.h:
class X {
public:
X();
virtual ~X();
void publicInterface();
void publicInterface2();
private:
struct Impl;
std::unique_ptr<Impl> impl;
}
X.cpp:
#include "X.h"
#include <windows.h>
struct X::Impl {
HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
// all private fields, methods go here
void privateMethod(HWND wnd);
void privateMethod();
};
X::X() : impl(new Impl()) {
// ...
}
// and the rest of definitions go here
如果完整的类定义需要来自某个外部库的类型定义,而这个库具有繁重或丑陋的头文件(使用WinAPI),这将特别有用。如果您使用PIMPL,则只能在.cpp
中包含任何特定于WinAPI的功能,并且不得将其包含在.h
中。
答案 2 :(得分:21)
我不太多使用嵌套类,但我偶尔会使用它们。特别是当我定义某种数据类型时,我想要定义一个为该数据类型设计的STL仿函数。
例如,考虑具有ID号,类型代码和字段名称的通用Field
类。如果我想通过ID号或名称搜索这些vector
的{{1}},我可以构建一个仿函数来执行此操作:
Field
然后,需要搜索这些class Field
{
public:
unsigned id_;
string name_;
unsigned type_;
class match : public std::unary_function<bool, Field>
{
public:
match(const string& name) : name_(name), has_name_(true) {};
match(unsigned id) : id_(id), has_id_(true) {};
bool operator()(const Field& rhs) const
{
bool ret = true;
if( ret && has_id_ ) ret = id_ == rhs.id_;
if( ret && has_name_ ) ret = name_ == rhs.name_;
return ret;
};
private:
unsigned id_;
bool has_id_;
string name_;
bool has_name_;
};
};
的代码可以使用Field
类本身范围内的match
:
Field
答案 3 :(得分:12)
One can implement a Builder pattern with nested class。特别是在C ++中,我个人认为它在语义上更清晰。例如:
class Product{
public:
class Builder;
}
class Product::Builder {
// Builder Implementation
}
而不是:
class Product {}
class ProductBuilder {}
答案 4 :(得分:0)
我认为使类嵌套而不仅仅是朋友类的主要目的是能够在派生类中继承嵌套类。 C++中没有继承友谊。
答案 5 :(得分:0)
您还可以考虑主函数的一流 ass 类型,您可以在其中启动所有需要的类来一起工作。例如类 Game,启动所有其他类,如窗口、英雄、敌人、级别等。这样你就可以摆脱主函数中的所有东西了。您可以在哪里创建 Game 对象,也可以进行一些与 Gemente 本身无关的额外外部调用。