Effective C++ 之 Item 6 : 若不想使用编译器自动生成的函数,就该明确拒绝

Effective C++

chapter 2. 构造 / 析构 / 赋值运算

(Constructors, Destructors, and Assignment Operators)

Item 6. 若不想使用编译器自动生成的函数,就该明确拒绝

(Explicitly disallow the use of compiler-generated functions you do not want)

地产中介商卖的是房子,一个中介软件系统自然而然想必有个 class 用来描述待售房屋:

class HomeForSale { ... };

地产商都认为每一笔资产都是独一无二的,因而为 HomeForSale 对象做一份副本有点没道理。因此应该乐意看到 HomeForSale 的对象拷贝动作以失败收场:

HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1);             //企图拷贝 h1  — — 不该通过编译
h1 = h2;                        //企图拷贝 h2  — — 也不该通过编译

阻止这一类代码的编译并不是很直观。通常如果不希望 class 支持某一特定机能,只要不声明对应函数就行。但这个策略对 copy 构造函数和 copy assignment 操作符却不起作用,因为 Item 5 已经指出,如果不声明而尝试调用它们,编译器会声明它们。

这便遇到一个困境。如果不声明 copy 构造函数或 copy assignment 操作符,编译器可能为你产出一份,于是你的 class 支持 copying。如果声明它们,你的 class 还是支持 copying。但这里的目标却是要阻止 copying!

答案的关键是,所有编译器产出的函数都是 public。为阻止这些函数被创建出来,得自行申明它们,但这里并没有什么需求使你必须将它们申明为 public。因此你可以将 copy 构造函数或 copy assignment 操作符申明为 private。藉由明确申明一个成员函数以阻止编译器暗自创建其专属版本;而令这些函数为 private,使你得以成功阻止人们调用它。

一般而言这个做法并不绝对安全,因为 member 函数和 friend 函数还是可以调用 private 函数。除非你不去定义它们,那么如果某些人不慎调用任何一个,会获得一个连接错误 (linkage error )。“将成员函数声明为 private 而且故意不实现它们”这一伎俩是如此为大家接受,因而被用在 C++ iostream 程序库中阻止 copying 行为。这个伎俩施行与 HomeForSale 也很简单:

class HomeForSale
{
public:
    ...
private:
    ...
    HomeForSale (const HomeForSale &);        //只有声明
    HomeForSale& operator= (const HomeForSale &);
};

有了上述 class 定义,当客户企图拷贝 HomeForSale 对象,编译器会阻挠他。如果你不慎在 member 函数或者 friend 函数之内那么做,轮到连接器发出抱怨。

将连接期错误转移至编译器是可能的(而且那是好事,毕竟俞早侦测出错误愈好),只要将 copy 构造函数和 copy assignment 操作符申明为 private 就可以办到,但不是在 HomeForSale 自身,而是在一个专门为了阻止 copying 动作而设计的 base class 内。这个 base class 非常简单:

class Uncopyable
{
protected:
     Uncopyable() { }             //允许 derived 对象构造和析构
    ~ Uncopyable() { }
private:
     Uncopyable (const  Uncopyable&);         //但阻止 copying
     Uncopyable& operator=(const  Uncopyable&);
};

为求阻止 HomeForSale 对象被拷贝,我们唯一需要做的就是继承  Uncopyable:

class HomeForSale: private  Uncopyable       //class 不再申明 copy 构造函数或 copy assignment 操作符
{
    ...
};

这行得通,因为任何(甚至是 member 函数或 friend 函数)尝试拷贝 HomeForSale 对象,编译器便试着生成一个 copy 构造函数和一个 copy assignment 操作符,而正如 Item 12 所说,这些函数的“编译器生成版”会尝试调用其 base class 的对应兄弟,那些调用会被编译器拒绝,因为其 base class 的拷贝函数是 private。

Uncopyable class 的实现和运用颇为微妙,包括不一定得以 public 继承它(见 Item 32 和 Item 39),以及  Uncopyable 的析构函数不一定得是 virtual (见 Item 7)等等。 Uncopyable 不含数据,因此符合 Item 39 所描述的 empty base class optimization 资格。但由于它总是扮演 base class,因此使用这项技术可能导致多重继承(因为你还可能继承其他 class,多重继承见 Item 40),而多重继承有时会阻止 empty base class optimization (再次见 Item 39)。通常可以忽略这些微妙点,只像上面那样使用 Uncopyable,因为它完全像“广告”所说的能够正常运行。也可以使用 Boost(见 Item 55) 提供的版本,名为 noncopyable 的 class。

请记住

  • 为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为 private 并且不予实现。使用像 Uncopyable 这样的 base class 也是一种做法。
时间: 2024-10-13 00:09:34

Effective C++ 之 Item 6 : 若不想使用编译器自动生成的函数,就该明确拒绝的相关文章

【Effective c++】条款6:若不想使用编译器自动生成的函数就应该明确拒绝

地产中介卖的是房子,其使用的中介软件系统应该有个类用来描述卖掉的房子 class HomeFoeSale { ......} 但是任何房子都是独一无二的,不应该存在两个房子拥有同样的属性,因此以下操作不应该正确! HomeForSale h; HomeForSale h1(h); //调用复制构造函数 HomeForSale h2 = h; //调用赋值操作符 阻止这两个操作(复制.赋值)可以不声明它们,but自己不声明,编译器会自动生成,并且访问权限还是public.没办法只好声明出来,但是如

Effective C++ Item 6 若不想使用编译器自动生成的函数,就该明确拒绝

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 为驳回编译器自动提供的机能,可将相应的成员函数声明为private并且不予实现.使用像Uncopyable这样的base class也是一种方法 classUncopyable{ protected: //允许derived对象构造和析构 Uncopyable(){} ~Uncopyable(){} private: Uncopyable(constUncopyable&); //但阻止c

条款6:不想使用编译器自动生成的函数,就要明确拒绝!

每一个对象都是独一无二的,如果不想其被复制,我们就希望其复制以失败收场.如一座房屋出售HomeForSale类: 1 HomeForSale h1; 2 HomeForSale h2; 3 HomeForSale h3(h1);//我们希望这俩语句以失败告终 4 h1=h2;// 通常情况下,我们使用某个功能时,调用相对应的函数即可,如果这个函数没有被定义,则编译器会提示错误.但是这一情况不适用复制构造函数和赋值构造函数.因为条款5已经指出,如果你不声明它们,而有人希望调用它们,编译器就会替你声

Effective C++ 条款6 若不想使用编译器自动生成的函数,就该明确拒绝

1. 某些类的含义决定了它们不具备某些功能,也就是说某些函数不能被创造出来以防被错误的使用(例如定义一个Book类,它含有一个表示ISBN的变量,这种情况下拷贝构造函数以及赋值操作符显然是没有意义的,因为任何两种书的ISBN都不同),但是编译器在类的创建者没有声明默认构造函数,拷贝构造函数,赋值操作符和析构函数的情况下会产生这些函数,为了避免这种情况,可以将这些函数声明为private并且不提供它们的定义来阻止它们的使用,但是如果这些函数经由其他成员函数或者友元函数调用,错误将会在链接期才能被发

effective c++ 条款06:若不想使用编译器自动生成的函数,就该明确拒绝

记住:为防止编译器暗自提供的功能,可将相应的成员函数声明为privae并且不予实现.也可以使用Uncopyable这样的父类实现. 对于独一无二的对象,希望不支持拷贝构造函数和赋值操作符. class HomeForSale { public: ... private: HomeForSale(const HomeForSale&); //只是声明,阻止编译器自动生成 HomeForSale& operator=(const HomeForSale&); } HomeForSale

若不想使用编译器自动生成的函数,就该明确拒绝

//有些情况下,想要明确地拒绝对象之间的拷贝,可以采用两种方式: //方式1: 将拷贝构造函数和赋值操作声明为private,且不去实现 class A { private: A(const A&); A& operator=(const A&); }; int main() { A a1; //报错:没有默认的构造函数, 一旦存在自定义的构造函数,即使是copy构造函数,编译器也不再会生成默认构造函数了. A a2 = a1; //报错:拷贝构造函数不可访问.return 0;

Effective C++ 条款六 若不想使用编译器自动生成的函数,就该明确拒绝

class HomeForSale //防止别人拷贝方法一:将相应的成员函数声明为private并且不予实现 { public: private: HomeForSale(const HomeForSale&); HomeForSale& operator = (const HomeForSale&);//只有申明,此函数很少被使用   };   //方法二,设计一个专门用来阻止copying动作的基类,然后让其他类继承这个类即可   class Uncopyable { prot

Effetive C++_笔记_条款06_若不想使用编译器自动生成的函数,就该明确拒绝

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 通常如果你不希望class支持某一特定机能,只要不声明对应函数就是了.但这个策略对copy构造函数和copy assignment操作符却不起作用,你如果不声明它们,而某些人尝试调用它,编译器会为你声明它们. 这把你逼到了一个困境.如果你不声明copy构造函数和copy assignment操作符,编译器可能为你产出一份,于是你的clas支持copying.如果

条款6:如果不想使用编译器自动生成的函数,就应该明确的拒绝。

有些情况自己是不希望生成拷贝构造函数以及拷贝赋值运算符的,这种时候不能只是自己不去编写这些函数,因为这样编译器会自动的去生成这些函数.保险一点的做法是将拷贝构造函数以及拷贝赋值运算符都声明为private的.这样既阻止了编译器生成默认的版本,而且又阻止了别人去调用它. 注意上面的这条“将成员函数声明为private而故意的不去实现它”是一种常用手段,即使是标准程序库中有的部分也是这样做的. class HomeForSale//很明显,销售的两个方子一般内容都是不相同的,所以拷贝构造函数以及 {