读书笔记 effective c++ Item 6 如果你不想使用编译器自动生成的函数,你需要明确拒绝
问题描述-阻止对象的拷贝
现实生活中的房产中介卖房子,一个服务于这个中介的软件系统很自然的会有一个表示要被销售的房屋的类:
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 如果你不想使用编译器自动生成的函数,你需要明确拒绝的更多相关文章
- Effective C++ -----条款06:若不想使用编译器自动生成的函数,就该明确拒绝
为驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不予实现. 使用像Uncopyable这样的base class也是一种做法(即先声明一个基类,然后私有继承它).这其实有点像使用 ...
- 【Effective c++】条款6:若不想使用编译器自动生成的函数就应该明确拒绝
地产中介卖的是房子,其使用的中介软件系统应该有个类用来描述卖掉的房子 class HomeFoeSale { ......} 但是任何房子都是独一无二的,不应该存在两个房子拥有同样的属性,因此以下操作 ...
- Effective C++ 条款六 若不想使用编译器自动生成的函数,就该明确拒绝
class HomeForSale //防止别人拷贝方法一:将相应的成员函数声明为private并且不予实现 { public: private: HomeForSale(const HomeForS ...
- Effective C++ 之 Item 6 : 若不想使用编译器自动生成的函数,就该明确拒绝
Effective C++ chapter 2. 构造 / 析构 / 赋值运算 (Constructors, Destructors, and Assignment Operators) Item 6 ...
- Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝
一.为驳回编译器自动提供的机能,可将相应成员函数声明为private并且不予实现.(如果你仅仅是自己不实现的话,编译器会帮你实现) 如: class A { public: A(const strin ...
- Effective C++_笔记_条款06_若不想使用编译器自动生成的函数,就该明确拒绝
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 通常如果你不希望class支持某一特定机能,只要不声明对应函数就 ...
- Effective C++ 条款06:若不想使用编译器自动生成的函数,就该明确拒绝
规则一 将成员函数声明为private而且故意不实现他们 class HomeForSale { public: ... private: ... HomeForSale(const HomeForS ...
- 读书笔记 effective c++ Item 52 如果你实现了placement new,你也要实现placement delete
1. 调用普通版本的operator new抛出异常会发生什么? Placement new和placement delete不是C++动物园中最常遇到的猛兽,所以你不用担心你对它们不熟悉.当你像下面 ...
- 读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库
1. C++0x的历史渊源 C++标准——也就是定义语言的文档和程序库——在1998被批准.在2003年,一个小的“修复bug”版本被发布.然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C+ ...
随机推荐
- 文本去重-----awk或者uniq
对于awk '!a[$3]++',需要了解3个知识点 1.awk数组知识,不说了 2.awk的基本命令格式 awk 'pattern{action}' 省略action时,默认action是{ ...
- JavaScript 事件模型 事件处理机制
什么是事件? 事件(Event)是JavaScript应用跳动的心脏 ,也是把所有东西粘在一起的胶水.当我们与浏览器中 Web 页面进行某些类型的交互时,事件就发生了.事件可能是用户在某些内容上的点击 ...
- WPF 制作电子相册浏览器
周末的时候,闲着无聊,做了一个电子相册浏览器.比较简单.界面如下: 主要部分代码如下: MainWindow.xaml <local:HeaderedWindow x:Class="P ...
- sqlserver 脚本方式导出数据到excel
use EntDataCenter go SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- =========================== ...
- 创建和使用MySQL计划事件
查看事件调度程序线程的状态: SHOW PROCESSLIST; 要启用和启动事件调度程序线程命令: SET GLOBAL event_scheduler = ON; 禁用和停止事件调度程序线程: S ...
- Django 自定义模版标签和过滤器
实现自定义过滤器 1. 创建register变量 在你的模块文件中,你必须首先创建一个全局register变量,它是用来注册你自定义标签和过滤器的, 你需要在你的python文件的开始处,插入几下代码 ...
- Php连接及读取和写入mysql数据库的常用代码
在这里我总结了常用的PHP连接MySQL数据库以及读取写入数据库的方法,希望能够帮到你,当然也是作为我自己的一个回顾总结. 1.为了更好地设置数据连接,一般会将数据连接所涉及的值定义成变量. $mys ...
- 为什么delphi控件前面都有t
控件的类名都有一个T字, 它是Type的第一个字母. 比如按钮就是TButton. 但在Delphi的控件面板上的并不带T字, 比如就是Button. 如果你把它放在窗体上, 默认名字则成为Butto ...
- QT移植
QT下载地址:http://download.qt.io/archive/qt/1.编译tslib(touch screen lib) 准备工作:确保以下工具安装完成 sudo apt-get ins ...
- 【转载】doxygen+graphviz生成代码文档
一.工具 doxygen:http://www.stack.nl/~dimitri/doxygen/download.html graphviz:http://www.graphviz.org/ 二. ...