Effective c++ 学习笔记(item6)
Item6: Explicitly disallow the use of compiler-generated functions you do not want
题外话:从Item1到Item6,item1是总括介绍性的章节,虽然泛泛而谈,但要都理解也是不容易的。item2到item5这开头的几个章节说实话作者描述的知识点相当多。反倒是item6这一章是真简单。当然如果要把文中提到的多重继承问题也引申的搞透,那样子算的话也不简单。
什么情况下我们不想要赋值函数,拷贝构造函数
Scott举了个很实在的例子,比如有二手房产中介要卖房子,那么平台会有用一个类对象来保存房子的信息。每个房子都是独一无二的,程序平台就希望能够避免这种对象之间的赋值拷贝。伪代码如下
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); //这是没有意义的,所以平台开发者本意是要规避这种代码
h1 = h2;//这也是要规避的
代码设计者是想规避这种赋值或者拷贝,但不是通过不定义赋值函数或者拷贝构造函数就可以规避的。在item5中已经说过了,即使你没有定义他们,编译器发现后面的代码会用到,编译器也会帮你创建的。所以这里为了避免这种操作,还得让编译器不要自动创建。
禁用编译器自己创建的拷贝构造函数,拷贝赋值函数
方法1:手动添加private的拷贝构造函数,拷贝赋值函数
- 方法1:程序员自己声明这两个函数(可以不要定义)。并且声明他们为private函数。这样编译器就不会帮你创建,同时因为你创建的是private接口,类外面就不会被使用到。
- 方法1的问题: 问题是类内部的调用,或者朋友类可能还会用到。这种情况下,编译能通过。但是因为你没有定义,在链接阶段,会提示程序员链接错误,链接器链接不到指定的函数。
- 通过方法1可以禁用这两个函数,即使编译通过,链接的时候也会提醒程序员。此时程序员需要在类内部或者朋友类里取消对这两个函数的调用。 最终达到禁用目的。
上述的这种技巧在c++的类库里经常用到。
方法2:不用手动定义,借用编译器的默认创建的函数具有递归特性,通过统一的父类来限制
之所以推荐这个方法是因为,Scott提到的一点编码经验,像方法1的错误让他越早暴露越好。现在是链接阶段暴露,如果能提前到编译阶段暴露会更好。
更好的方法如下: 定义一个基类,让目标类继承这个基类。为了禁用目标类自动产生拷贝赋值函数,和拷贝构造函数可以利用“把基类的这两个函数”设置为private的方法。这种方法的原理是。当编译器发现目标类没有定义拷贝构造函数和拷贝赋值函数时,尝试自动创建。自动创建时需要递归调用父类的对应函数实现父类成分的操作。只要有一个父类对应拷贝赋值或者拷贝构造函数声明为private,那么编译器就会报错。
class UnCopyable
{
protected:
Uncopyable(){};
~unCopyable(){};
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
}
class HomeForSale : private Uncopyable
{}
另外文中示例是让目标类private继承UCopyable。当然这里public继承也没有什么问题。 private继承和public继承的区别可以见引申章节
item6的引申
private protected public 区别
注意private protedted public的限定有两场场景,一种是对类成员数据或接口的限定,一种是对继承方式的限定。两个限定的含义是不同的。
在定义成员接口或数据的时候,有三种限定。 public protected private这三种限定词是对访问权限的限定。通俗的理解是public是完全公开的声明,这个声明是针对编译器的,意味着告诉编译器要允许任何情况的该类型接口或数据调用。无论你是在类内部调用public接口或数据,还是在类外面通过类的实例调用,编译器都可以编译通过。protected声明是告诉编译器,可以认可我家族内部的调用,家族外部的调用一律不允许。private声明是告诉编译器这个是秘密只能允许我当事人一个人调用。
换个说法可以加深下理解:
- 对于public定义的接口或数据,可以在类内部,继承类内部直接使用,也可以在任何地方用对象实例调用如
oneClass.abc
或者pOneClass->abc
。 - 对于protected定义的接口或者数据,只能在类内部或者继承类内部直接使用,不允许用对象实例调用如
oneClass.abc
或者pOneClass->abc
。 - 对于private定义的接口或者数据,只能在类内部直接使用,其他方式都不允许。
在继承方式上,public要体现is-a的关系, private要体现has-a的关系,且很少使用protected继承。这个具体在item30-item40上描述。
boost库的使用
参考文档: www.boost.org
这里简单说下linux下如何使用
1)获取boost源码, 最新的源码维护在jfrog上,不过sourceForge上也能下载到。具体见官网"getting started"系列的手册。并解压到/home/XXX/
目录下
2)大部分的boost库只有头文件,头文件中包含了需要的模板和内联函数,并不需要依赖于其他库,这就为我们使用boost库提供了极大的方便,代码上只要加入#include语句,编译器设置上能找到就ok了。当然大部分并不是全部。还有一些库是有cpp文件的,还有非常少的一些也是有依赖的。
3)在代码中需要boost的地方声明头文件和必要的命名空间代码如下
#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
int main()
{
using namespace boost::lambda;
typedef std::istream_iterator<int> in;
std::for_each(in(std::cin),in(),std::cout<<(_1*3)<<"");
}
- 使用如下的编译命令 `g++ -I /home/XXX/boost1.77 main.cpp
网友评论