# Item 6: 如果你不想使用 compiler-generated functions(编译器生成函数),就明确拒绝
作者:Scott Meyers
译者:fatalerror99 (iTePub's Nirvana)
发布:http://blog.csdn.net/fatalerror99/
房地产代理商出售房屋,服务于这样的代理商的软件系统自然要有一个 class(类)来表示被出售的房屋:
```
class HomeForSale { ... };
```
每一个房地产代理商都会很快指出,每一件房产都是独特的——没有两件是完全一样的。在这种情况下,为 HomeForSale object(对象)做一个 copy(拷贝)的想法就令人不解了。你怎么能拷贝一个独一无二的东西呢?因此最好让类似这种企图拷贝 HomeForSale object(对象)的行为不能通过编译:
```
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); // attempt to copy h1 — should
// not compile!
h1 = h2; // attempt to copy h2 — should
// not compile!
```
唉,防止这种编译的方法并非那么简单易懂。通常,如果你不希望一个 class(类)支持某种功能,你可以简单地不声明赋予它这种功能的函数。这个策略对于 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)不起作用,因为,就象 Item 5 中指出的,如果你不声明它们,而有人又想调用它们,编译器就会替你声明它们。
这就限制了你。如果你不声明 copy constructor(拷贝构造函数)或 copy assignment operator(拷贝赋值运算符),编译器也可以替你生成它们。你的 class(类)还是会支持 copying(拷贝)。另一方面,如果你声明了这些函数,你的 class(类)依然会支持 copying(拷贝)。而我们此时的目的却是 prevent copying(防止拷贝)!
解决这个问题的关键是所有的编译器生成的函数都是 public(公有)的。为了防止生成这些函数,你必须自己声明它们,但是你没有理由把它们声明为 public(公有)的。相反,应该将 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)声明为 private(私有)的。通过显式声明一个 member function(成员函数),可以防止编译器生成它自己的版本,而且将这个函数声明为 private(私有)的,可以防止别人调用它。
通常,这个方案并不十分保险,因为 member(成员)和 friend functions(友元函数)还是能够调用你的 private 函数。换句话说,除非你十分聪明地不 define(定义)它们。那么,当有人不小心地调用了它们,在 link-time(连接时)会出现错误。这个窍门——声明 member functions(成员函数)为 private 却故意不去实现它——确实很好,在 C++ 的 iostreams 库里,就有几个类用此方法 prevent copying(防止拷贝)。比如,看一下你用的标准库的实现中 ios_base,basic_ios 和 sentry 的 definitions(定义),你就会看到 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)被声明为 private 而且没有被定义的情况。
将这个窍门用到 HomeForSale 上,很简单:
```
class HomeForSale {
public:
...
private:
...
HomeForSale(const HomeForSale&); // declarations only
HomeForSale& operator=(const HomeForSale&);
};
```
你会注意到,我省略了 functions' parameters(函数参数)的名字。这不是必须的,只是一个普通的惯例。毕竟,函数不会被实现,更少会被用到,有什么必要指定参数名呢?
对于上面的 class definition(类定义),编译器将阻止客户拷贝 HomeForSale objects(对象)的企图,如果你不小心在 member(成员)或 friend function(友元函数)中这样做了,连接程序会提出抗议。
将 link-time error(连接时错误)提前到编译时间也是可行的(早发现错误毕竟比晚发现好),通过不在 HomeForSale 本身中声明 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)为 private,而是在一个为 prevent copying(防止拷贝)而特意设计的 base class(基类)中声明。这个 base class(基类)本身非常简单:
```
class Uncopyable {
protected: // allow construction
Uncopyable() {} // and destruction of
~Uncopyable() {} // derived objects...
private:
Uncopyable(const Uncopyable&); // ...but prevent copying
Uncopyable& operator=(const Uncopyable&);
};
```
为了阻止拷贝 HomeForSale objects(对象),我们现在必须让它从 Uncopyable 继承:
```
class HomeForSale: private Uncopyable { // class no longer
... // declares copy ctor or
}; // copy assign. operator
```
这样做是因为,如果有人——甚至是 member(成员)或 friend function(友元函数)——试图拷贝一个 HomeForSale objects(对象),编译器将试图生成一个 copy constructor(拷贝构造函数)和一个 copy assignment operator(拷贝赋值运算符)。就象 Item 12 解释的,这些函数的 compiler-generated versions(编译器生成版)会试图调用 base class(基类)的相应函数,而这些调用将被拒绝,因为在 base class(基类)中,拷贝操作是 private(私有)的。
Uncopyable 的实现和使用包含一些微妙之处,比如,从 Uncopyable 继承不必是 public(公有)的(参见 Item 32 和 39),而且 Uncopyable 的 destructor(析构函数)不必是 virtual(虚拟)的(参见 Item 7)。因为 Uncopyable 不包含数据,所以它符合 Item 39 描述的 empty base class optimization(空基类优化)的条件,但因为它是 base class(基类),此项技术的应用不能引入 multiple inheritance(多继承)(参见 Item 40)。反过来说,multiple inheritance(多继承)有时会使 empty base class optimization(空基类优化)失效(还是参见 Item 39)。通常,你可以忽略这些微妙之处,而且仅仅像此处演示的这样来使用 Uncopyable,因为它的工作就像在做广告。你还可以使用在 Boost(参见 Item 55)中的一个可用版本。那个 class(类)名为 noncopyable。那是一个好东西,我只是发现那个名字有点儿 un-(不……)嗯…… nonnatural(非自然)。
Things to Remember
* 为了拒绝编译器自动提供的机能,将相应的 member functions(成员函数)声明为 private,而且不要给出 implementations(实现)。使用一个类似 Uncopyable 的 base class(基类)是方法之一。
- Preface(前言)
- Introduction(导言)
- Terminology(术语)
- Item 1: 将 C++ 视为 federation of languages(语言联合体)
- Item 2: 用 consts, enums 和 inlines 取代 #defines
- Item 3: 只要可能就用 const
- Item 4: 确保 objects(对象)在使用前被初始化
- Item 5: 了解 C++ 为你偷偷地加上和调用了什么函数
- Item 6: 如果你不想使用 compiler-generated functions(编译器生成函数),就明确拒绝
- Item 7: 在 polymorphic base classes(多态基类)中将 destructors(析构函数)声明为 virtual(虚拟)
- Item 8: 防止因为 exceptions(异常)而离开 destructors(析构函数)
- Item 9: 绝不要在 construction(构造)或 destruction(析构)期间调用 virtual functions(虚拟函数)
- Item 10: 让 assignment operators(赋值运算符)返回一个 reference to *this(引向 *this 的引用)
- Item 11: 在 operator= 中处理 assignment to self(自赋值)
- Item 12: 拷贝一个对象的所有组成部分
- Item 13: 使用对象管理资源
- Item 14: 谨慎考虑资源管理类的拷贝行为
- Item 15: 在资源管理类中准备访问裸资源(raw resources)
- Item 16: 使用相同形式的 new 和 delete
- Item 17: 在一个独立的语句中将 new 出来的对象存入智能指针
- Item 18: 使接口易于正确使用,而难以错误使用
- Item 19: 视类设计为类型设计
- Item 20: 用 pass-by-reference-to-const(传引用给 const)取代 pass-by-value(传值)
- Item 21: 当你必须返回一个对象时不要试图返回一个引用
- Item 22: 将数据成员声明为 private
- Item 23: 用非成员非友元函数取代成员函数
- Item 24: 当类型转换应该用于所有参数时,声明为非成员函数
- Item 25: 考虑支持不抛异常的 swap
- Item 26: 只要有可能就推迟变量定义
- Item 27: 将强制转型减到最少
- Item 28: 避免返回对象内部构件的“句柄”
- Item 29: 争取异常安全(exception-safe)的代码
- Item 30: 理解 inline 化的介入和排除
- Item 31: 最小化文件之间的编译依赖
- Item 32: 确保 public inheritance 模拟 "is-a"
- Item 33: 避免覆盖(hiding)“通过继承得到的名字”
- Item 34: 区分 inheritance of interface(接口继承)和 inheritance of implementation(实现继承)
- Item 35: 考虑可选的 virtual functions(虚拟函数)的替代方法
- Item 36: 绝不要重定义一个 inherited non-virtual function(通过继承得到的非虚拟函数)
- Item 37: 绝不要重定义一个函数的 inherited default parameter value(通过继承得到的缺省参数值)
- Item 38: 通过 composition(复合)模拟 "has-a"(有一个)或 "is-implemented-in-terms-of"(是根据……实现的)
- Item 39: 谨慎使用 private inheritance(私有继承)
- Item 40: 谨慎使用 multiple inheritance(多继承)
- Item 41: 理解 implicit interfaces(隐式接口)和 compile-time polymorphism(编译期多态)
- Item 42: 理解 typename 的两个含义
- Item 43: 了解如何访问 templatized base classes(模板化基类)中的名字
- Item 44: 从 templates(模板)中分离出 parameter-independent(参数无关)的代码
- Item 45: 用 member function templates(成员函数模板) 接受 "all compatible types"(“所有兼容类型”)
- Item 46: 需要 type conversions(类型转换)时在 templates(模板)内定义 non-member functions(非成员函数)
- Item 47: 为类型信息使用 traits classes(特征类)
- Item 48: 感受 template metaprogramming(模板元编程)
- Item 49: 了解 new-handler 的行为
- Item 50: 领会何时替换 new 和 delete 才有意义
- Item 51: 编写 new 和 delete 时要遵守惯例
- Item 52: 如果编写了 placement new,就要编写 placement delete
- 附录 A. 超越 Effective C++
- 附录 B. 第二和第三版之间的 Item 映射