有人可以将我引向一些不错的资源来理解和使用嵌套类吗?我有一些像《编程原理》之类的材料,以及诸如《IBM Knowledge Center-Nested Classes》之类的东西。
但是我仍然很难理解他们的目的。有人可以帮我吗?
typedef
。3.因为他们增加缩进一个附加级别的环境下,避免排长队已经很难4.因为你是在一个声明两个概念上不同的对象class
申报等
有人可以将我引向一些不错的资源来理解和使用嵌套类吗?我有一些像《编程原理》之类的材料,以及诸如《IBM Knowledge Center-Nested Classes》之类的东西。
但是我仍然很难理解他们的目的。有人可以帮我吗?
typedef
。3.因为他们增加缩进一个附加级别的环境下,避免排长队已经很难4.因为你是在一个声明两个概念上不同的对象class
申报等
Answers:
嵌套类非常适合隐藏实现细节。
清单:
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的一部分并且必须永久维护,所以这会妨碍我更新我的类。通过将类设为私有,我不仅隐藏了实现,而且还说这是我的,我可以随时更改它,以便您不能使用它。
看一下,std::list
或者std::map
它们都包含隐藏的类(或者它们是吗?)。关键是它们可能存在或不存在,但是因为实现是私有的并且是隐藏的,所以STL的构建者能够在不影响您使用代码的方式的情况下更新代码,或者因为需要它们而在STL周围留下了很多旧包。为了与那些决定使用隐藏在内部的Node类的傻瓜保持向后兼容list
。
Node
根本不应该在头文件中公开它。
detail
约定:与其依赖于这样的约定,不如记住自己,最好依靠编译器为您跟踪它们。
嵌套类与常规类一样,但是:
一些例子:
假设您想拥有一个SomeSpecificCollection
将聚集class对象的类Element
。然后,您可以:
声明两个类:SomeSpecificCollection
和Element
-不好,因为名称“ Element”足够通用,可能导致名称冲突
引入命名空间someSpecificCollection
并声明类someSpecificCollection::Collection
和someSpecificCollection::Element
。没有名称冲突的风险,但是它还能变得更加冗长吗?
声明两个全局类SomeSpecificCollection
和SomeSpecificCollectionElement
-有一些小缺点,但可能还可以。
声明全局类SomeSpecificCollection
和Element
作为其嵌套类的类。然后:
SomeSpecificCollection
您将just 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);
然后,通过输入Product::ProductType::
IDE,只能从建议的作用域中获取枚举。这也减少了犯错的风险。
当然,对于小型类而言,这可能不是必需的,但是如果一个类有很多枚举,那么对于客户端程序员来说,事情变得更容易了。
同样,如果需要,您可以在模板中“组织”一大堆typedef。有时这是一个有用的模式。
PIMPL(指针到IMPLementation的缩写)是一种惯用法,可用于从标头中删除类的实现细节。每当头的“实现”部分更改时,这都减少了根据类的头重新编译类的需求。
通常使用嵌套类来实现:
Xh:
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,则只能将任何特定于WinAPI的功能封装在中,.cpp
而永远不要将其包括在内.h
。
struct Impl; std::auto_ptr<Impl> impl;
该错误已由Herb Sutter推广。不要在不完整的类型上使用auto_ptr,或者至少要采取预防措施以避免生成错误的代码。
auto_ptr
在大多数实现中声明一个不完整的类型,但是从技术上讲,它是UB,与C ++ 0x中的某些模板(例如unique_ptr
)不同,在该模板中已明确指出template参数可能是一个不完整的类型,以及该类型必须完整的位置。(例如使用~unique_ptr
)
T
的unique_ptr
可能是一个不完整的类型。”
enum class
。
我没有太多使用嵌套类,但是我时不时地使用它们。尤其是当我定义某种数据类型时,然后我想定义为该数据类型设计的STL函子。
例如,考虑Field
具有ID号,类型代码和字段名称的通用类。如果我想搜索vector
这些中Field
的任一ID号码或姓名S,我可能会构建一个仿函数可以这样做:
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
:
vector<Field>::const_iterator it = find_if(fields.begin(), fields.end(), Field::match("FieldName"));
可以使用嵌套类实现Builder模式。特别是在C ++中,我个人觉得它在语义上更简洁。例如:
class Product{
public:
class Builder;
}
class Product::Builder {
// Builder Implementation
}
而不是:
class Product {}
class ProductBuilder {}