整本书,我只想写一篇博客,只注重概要,例子不会太多,主要是说思想,短小精悍是最好的。
这本书以前读过,只是觉得当时看得不仔细,现在想回头再看看这个东西。
1.让自己习惯c++
这一部分算是c++的一个导读吧,可以作为一个引子,其实并没有涉及什么c++的技术细节,只是给了概览性的一个叙述,在这里还不需要深厚的c++功底。
条款01:视C++为一个语言联邦
c++是c plus plus的一个简写,望文生义的来说的话,它是比C多了些东西。除了面向过程,它还能面向对象,当然了,面向对象之前还有基于对象的方式;此外,它还支持函数形式,泛型和元编程。
泛型是一种思想,它的实现可以参照STL,里面的容器,算法,迭代器和函数对象(也即functor)等机制。boost也是不可忽略的一个庞大的库,对于C++11的主要来源,11里的24个新特性从boost里获取了15个,它的重要性可见一斑。在看STL源码解析的时候,觉得STL很大程度上解决了编程时的通用代码的编写,主要是里面的容器和算法,但是有时候还需要自己写点儿数据结构和算法的东西;但接触了boost之后,虽然没有完全的不用自己再写东西,但是里面的smart_ptr,bind和function,multi-index和hash等等确实比STL更加方便了,boost是完全和STL兼容的。
条款02:用const,enum和inline替换#define
宁可以编译器替换预处理器比较好。开篇就看到这句话了,define的东西是在预处理阶段进行替换的,在程序的编译阶段,这些东西已经没了踪影。若是改用const等,从编译到实际的调试过程中,都可以看到这些名称。这里有两个例外情况:
第一,定义常量指针:有两个地方可以写const,分别是*之前和之后,之前的是对指向的const,之后是对指针自身。
第二,class专属的常量,最好是弄成static的,避免一个每个实例都有一份。g++的稍高一点儿的版本貌似都支持基本类型在声明时直接赋值了。static的东西,若是不赋初值,默认为0;
enum更像define,不能取址,只能取值。它还是tmp的基础技术,等了解了template metaprogramming技术了再更好的理解它吧,目前只能就它在C里的用法来了。
inline和define都能定义函数,但是inline更oo一些,而且不用再去考虑括号是否漏掉了一个等等等。
这里就出来了一个规则了,对于:
简单常量,const或enum更好,enum更适合有多重取值的情况;
对于小函数,inline就是绝配了。
条款03:尽量使用const
这个条款没什么可说的觉得,只是在你需要恒定不变的地方加上const,可以是在类,namespace,operator,指针,迭代器,reference,和函数以及局部和全局的变量等等。
两个函数,如果只是constness不同,是可以被重载的。constness就是函数末尾写的那个const。比如一个operator[],当是constness的时候,它可以用于const对象;但是constness的[]不能做左值,你懂的。
当我们声明一个函数为const的时候,默认它是bitwise constness,这样如果有个char *p成员变量,它指向一个buffer,虽然[]为constness,但是如果用一个char*去指向p,之后对p的任何修改都是符合bitwise constness的,这是因为指针而非所指物是成员。这种情况即所谓的logical constness。那么编译器坚持bitwise constness的情况下,如果有成员表示p的大小,在一个const 函数中,是不允许修改这样的变量的,可以用mutable做个修饰,释放掉non-static成员变量的bitwise constness的约束。
在constness和non-constness中避免重复:如果一个同名、同参数的函数,只是constness不一样,其它要做的事情完全相同,只是返回值的constness的差别;但是我们还要有两份完全一样的函数体,代码是重复的,耗费了双倍的编译时间,代码体积也进而膨胀,可以考虑使用转型(casting),从non-const函数或operator里调用const version,在调用之前和之后都要做下const转换。为什么不用const调用non-const呢?那个const是不能改动对象的,除了mutable的,但是你却可能在non-const时改动对象的内容。
注意:
1.将sth声明为const可以帮助编译器侦测出错误用法。const可以用于前述的一对东西。
2.编译器认为的是bitwise constness,但是程序员应该使用的却是conceptual constness。
3.当一个成员函数或operator有两个const和non-const两个版本时,用non-const调const版本可以避免代码重复;但是反之会引入const版本里改动对象内容的风险。
条款04:确定对象在使用前已被初始化
永远在使用对象之前将其初始化。
1.对于语言内置类型,要手工完成。
2.内置类型之外的东西,初始化发生在构造函数身上。
构造函数的调用分两步:
第一步是初始化,即对参数列表,构造函数后面加个冒号的那个东东;
第二部是赋值,发生在构造函数体内;
所以,对非内置类型,初始化的时候会调用默认constructor,要是写在构造函数体内,就会调默认构造和copy assignment operator,所以初始化列表的方式是最好的,只调copy constructor。对内置类型,写到哪儿倒是都一样,初始化和赋值的成本无异,不过为了和非内置统一,都初始化列表吧。
要分得清楚什么是拷贝构造和拷贝复制操作符,这两个概念在essential cpp里面就讲过了,不再赘述哈。见谅。
构造函数初始化变量的顺序,和构造函数的参数列表的顺序无关,但和定义类时写的参数的顺序有关,为了统一,最好保持二者的一致性。
如果成员变量是const或reference,则必须初始化,不能够被赋值。
不同编译单元之间不要使用对方的non-local static 对象,因为被使用的对象可能未初始化,C++对不同编译单元之间的non-local static对象的初始化顺序没有明确定义。为避免在对象初始化之前过早的使用他们,需要做到:
第一、手工初始化内置型non-member对象。
第二、使用成员初始化列表对付对象的所有成分。
最后,基于non-local static的初始化次序不确定来进行设计。
注意:
1、为内置型对象手工初始化,因为C++不保证初始化它们。
2、构造函数最好使用memeber initialization list,而不要再constructor内使用assignment。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
3、为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。
2.构造、析构、赋值运算
条款05:了解C++默默编写并调用哪些函数
当你写了一个空的class的时候,编译器会默认为你构造四个函数或operator:默认构造函数,拷贝构造函数,析构函数和copy assignment operator。
但当你写了一个带实参的构造函数时,编译器将不会为你自动添加默认构造函数。
条款06:若不想使用编译器自动生成的函数,就该明确拒绝
若是不想一个类的对象被拷贝,就把它的copy constructor和copy assignment operator声明为private,并且不予实现,这样当出现拷贝行为时会报错;
若是在该类的成员函数或者是friend的东西里调到他时,也会报错,但是是在链接时,要想把错误移到编译时,可以继承自一个父类,把着两个设为private,并且是private继承。典型的这种父类即boost::noncopyable。
继承是否非要是private才可以嗯,明天试一下再添加进来
条款07:为多态基类声明virtual destructor
条款08:别让异常逃离destructor
条款09:绝不在构造和析构过程中调用virtual函数
条款10:令operator=返回一个reference to *this
条款11:在operator=中处理"自我赋值"
条款12:复制对象时勿忘其每一个成分
3资源管理
条款13:以对象管理资源
条款14:在资源管理类中小心coping行为
条款15:在资源管理类中提供对原始资源的访问
条款16:成对使用new和delete时要采取相同的形式
条款17:以独立语句将newd对象置入智能指针
4设计与声明
条款18:让接口容易被正确使用,不易被误用
条款19:设计class犹如设计type
条款20:宁以pass by reference to const替换pass by value
条款21:必须返回对象时,别妄想返回其reference
条款22:将成员变量声明为private
条款23:宁以non-member、non-friend替换member函数
条款24:若所有参数皆需类型转换,请为此采用non-member函数
条款25:考虑写出一个不抛异常的swap函数
5实现
条款26:尽可能延后变量定义式的出现时间
条款27:尽量少做转型动作
条款28:避免返回handles指向对象内部成分
条款29:为"异常安全"而努力是值得的
条款30:彻底了解inlining的里里外外
条款31:将文件间的编译依存关系降至最低
6继承与面向对象设计
条款32:确定你的public继承塑模出is-a关系
条款33:避免遮掩继承而来的名称
条款34:区分接口继承和实现继承
条款35:考虑virtual函数以外的其他选择
条款36:绝不重新定义继承而来的non-virtual函数
条款37:绝不重新定义继承而来的缺省参数值
条款38:通过复合塑模出has-a或"根据某物实现出"
条款39:明智而审慎地使用多重继承
7模板与泛型编程
条款41:了解隐式接口和编译期多态
条款42:了解typename的双重意义
条款43:学习处理模板化基类内的名称
条款44:将与参数无关的代码抽离templates
条款45:运用成员函数模板接收所有兼容类型
条款46:需要定义类型转换时请为模板定义非成员函数
条款47:请使用traits classes表现类型信息
条款48:认识template元编程
8定制new和delete
条款49:了解new-handler的行为
条款50:了解new和delete的合理替换时机
条款51:编写new和delete时需固守常规
条款52:写了placement new,也要写placement delete
9杂项讨论
条款53:不要轻忽编译期的警告
条款54:让自己熟悉包括TR1在内的标准程序库
条款55:让自己熟悉Boost
to be continued