问题描述-阻止对象的拷贝

现实生活中的房产中介卖房子,一个服务于这个中介的软件系统很自然的会有一个表示要被销售的房屋的类:

 class HomeForSale { ... };

每个房产中介会立刻指出来,要销售房屋的每个属性都是唯一的,没有两个完全一样的房屋。在这种情况下,拷贝一个HomeForSale对象就没有任何意义了。你在怎么能拷贝一些独一无二的东西呢?因此你可能会尝试,如果有拷贝HomeForSale对象的函数,代码将不能够通过编译。

 HomeForSale h1;

 HomeForSale h2;

 HomeForSale h3(h1); // attempt to copy h1 — should

 // not compile!

 h1 = h2; // attempt to copy h2 — should

 // not compile!

阻止这样的编译不是简简单单能够做到的。在通常情况下,如果你不想一个类支持特定类别的函数,你只要不声明这个函数就可以了。但是这个策略对拷贝构造函数和拷贝赋值运算符来说就不工作了,因为正如Item5指出的:如果你没有声明这两个函数的情况下,如果一些人尝试去调用它们,编译器会自动声明它们。

你遇到麻烦了,如果你没有声明拷贝构造函数或者拷贝赋值运算符,编译器会为你生成。你的类于是会支持拷贝。如果你声明了它们,你的类还是会支持它们。但是你的目标是阻止拷贝!

阻止对象拷贝方法一-将拷贝构造函数和赋值运算符声明为private,并且不去实现它们

解决方案的关键在于所有编译器生成的函数都是public的。为了阻止这些函数被生成,你必须自己声明它们,但是不需要你将它们声明成public的。而是把拷贝构造函数和拷贝赋值运算符声明成private。通过显示的声明一个函数,就会阻止编译器生成它们自己的版本,同时,通过将函数声明成private,你也能够阻止人来调用它们。

这个方法不是十分安全的,因为类成员函数和友元函数仍然能够访问你的私有函数。除非你够聪明而不去定义这些函数。这样如果有人无意调用它们,就会得到一个链接错误。将函数声明成private而不去定义它的诡计如此被大家接受,它常常用在c++ 的iostreams库中,用于阻止类对象之间的拷贝。你可以看一下,在你的标准库的实现中,ios_base,basic_ios和sentry的定义处。你就会发现在每种情况中,拷贝构造函数和拷贝赋值运算符都被声明成private的并且没有被定义。

将这个伎俩用于HomeSale很简单:

 1 class HomeForSale {
2
3 public:
4
5 ...
6
7 private:
8
9 ...
10
11 HomeForSale(const HomeForSale&); // declarations only
12
13 HomeForSale& operator=(const HomeForSale&);
14
15 };

你会发现这里我将函数的参数省略掉了。这不是必须的,只是一个约定俗成的东西。毕竟,这个函数永远不会被实现,更不用说被使用了,因此指定函数参数没有什么用处。

使用上面的类定义,编译器就会阻止客户端尝试拷贝HomeForSale对象,如果你无意的在一个成员函数或者友元函数中这么做了,连接器会发出抱怨。

阻止对象拷贝方法二-将函数的私有声明提到特定基类

将拷贝构造函数和拷贝赋值运算符声明为private的位置由HomeForSale类替换为一个专门设计的基类,同样能够阻止拷贝,并且可以将链接才能发现的错误移动到在编译期就能够发现(这是好事,早点发现错误比晚发现要好)。

 class Uncopyable {

 protected: // allow construction

 Uncopyable() {} // and destruction of

 ~Uncopyable() {} // derived objects...

 private:

 Uncopyable(const Uncopyable&); // ...but prevent copying

 Uncopyable& operator=(const Uncopyable&);

 };

为了阻止HomeForSale被拷贝,我们需要做的是继承Uncopyable.

 class HomeForSale: private Uncopyable { // class no longer

 ... // declares copy ctor or

 }; // copy assign. Operator

这个方法也是行得通的,因为编译器会尝试生成一个拷贝构造函数和一个拷贝赋值运算符如果任何人(包括成员函数或者友元函数)尝试拷贝一个HomeForSale对象。在Item12中解释到,这些函数的编译器生成版本会调用基类的对应的部分,这样调用就会被拒绝,因为基类中的拷贝操作都是private的。

Uncopyable的一些使用和实现有一些微妙的地方,像从Upcopyable继承不必是public继承(Item32和Item39)Uncopyable的析构函数不必是虚函数。因为Uncopyable没有包含任何数据,因此符合Item39描述的,它是进行空基类优化(empty base class optimization)的合格者.一般来说,你可以忽略这些微妙之处,按照展示的使用就可以了,它会恰到好处的工作。你也可以使用Boost库中的版本,名字叫做noncopuable。这是一个很好的类,只是名字看上去不太自然。

读书笔记 effective c++ Item 6 如果你不想使用编译器自动生成的函数,你需要明确拒绝的更多相关文章

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

    为驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不予实现. 使用像Uncopyable这样的base class也是一种做法(即先声明一个基类,然后私有继承它).这其实有点像使用 ...

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

    地产中介卖的是房子,其使用的中介软件系统应该有个类用来描述卖掉的房子 class HomeFoeSale { ......} 但是任何房子都是独一无二的,不应该存在两个房子拥有同样的属性,因此以下操作 ...

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

    class HomeForSale //防止别人拷贝方法一:将相应的成员函数声明为private并且不予实现 { public: private: HomeForSale(const HomeForS ...

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

    Effective C++ chapter 2. 构造 / 析构 / 赋值运算 (Constructors, Destructors, and Assignment Operators) Item 6 ...

  5. Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝

    一.为驳回编译器自动提供的机能,可将相应成员函数声明为private并且不予实现.(如果你仅仅是自己不实现的话,编译器会帮你实现) 如: class A { public: A(const strin ...

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

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 通常如果你不希望class支持某一特定机能,只要不声明对应函数就 ...

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

    规则一 将成员函数声明为private而且故意不实现他们 class HomeForSale { public: ... private: ... HomeForSale(const HomeForS ...

  8. 读书笔记 effective c++ Item 52 如果你实现了placement new,你也要实现placement delete

    1. 调用普通版本的operator new抛出异常会发生什么? Placement new和placement delete不是C++动物园中最常遇到的猛兽,所以你不用担心你对它们不熟悉.当你像下面 ...

  9. 读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库

    1. C++0x的历史渊源 C++标准——也就是定义语言的文档和程序库——在1998被批准.在2003年,一个小的“修复bug”版本被发布.然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C+ ...

随机推荐

  1. javascript之日期对象

    学习要点: 日期对象 将日期对象转换为字符串 将日期对象中的日期和时间转换为字符串 日期对象中的日期 日期对象中的时间 设置日期对象中的日期 设置日期对象中的时间 与毫秒相关的方法 一.日期对象 在j ...

  2. 《算法导论》习题2.3-7 查找集合S中是否有两个元素和为X---Java实现

    代码如下: public class MergeSort { public static void sort(int [] A,int p, int r) { if(p<r) { int q = ...

  3. jQuery.extend({...})分析

    作者:zccst 看一下是如何写的 jQuery.extend({ prop:"" method:function(){} }); 可以看出,这些方法是jQuery的静态属性和方法 ...

  4. AndroidStudio项目.gitignore文件内容

    .metadata/ *~ # files for the dex VM *.dex # Java class files *.class # generated files bin/ gen/ li ...

  5. Elasticsearch的使用场景深入详解

    了解了ES的使用场景,ES的研究.使用.推广才更有价值和意义. 1.场景-:使用Elasticsearch作为主要的后端 传统项目中,搜索引擎是部署在成熟的数据存储的顶部,以提供快速且相关的搜索能力. ...

  6. java 读写excle

    2014-04-16 20:38:20 java读写excel 晚上打算研究如何c来编写

  7. Python3基础 add() 向集合中加入新的元素

    镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...

  8. CodeForces 757D Felicity's Big Secret Revealed(状压DP)

    题意:给定一个01串,一个有效的n切割定义如下:一个横杠代表一次切割,第一条横杠前面的01串不算,最后一条横杠后面的01串不算,将两个横杠中的01串转化成十进制数字,假设这些数字的最大值是MAX且这些 ...

  9. Linux下的文件目录结构详解

    Linux下的文件目录结构详解 / Linux文件系统的上层根目录 /bin 存放用户可执行的程序 /boot 操作系统启动时所需要的文件 /dev 接口设备文件目录,例如:had表示硬盘 /etc ...

  10. JQuery的$.each(list,function(i,a){} 中 i和a 未定义,a取不到值

    $.each(list,function(i,a){ var status = ""; if(a.status==0) status="未审核"; else i ...