本文内容一部分是自己理解,一部分是《Effective C++》中节选内容,若想阅读详细实例和分析,请阅读《Effective C++》。
注:其中标题和总结部分引用原文内容。
1、视C++为一个语言联邦
1)多重范性编程语言:面向过程、面向对象、函数编程、泛型编程、元编程;
2)主要分为四部分次语言:C、Object-Oriented C++、Template C++、STL;
3)STL主要偏向于C。而模板则更多是C++,所以前者主要使用指针,后者最好使用const的引用,或者可以使用值类型。
总结:C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。
2、尽量以const ,enum,inline替换#define
1)当我们定义一个常量时,最好使用const常量,而非#define,因为define会无脑的替换,这个常量可以是专属于某处的,而#define所定义的是全局的;
2)使用enum可以更好的作为int类型的变量使用;
3)有时候我们会用#define来定义一个函数,这样做很方便而且没有调用函数的开销,但是这样的函数很容易出问题。而我们使用inline函数,加上模板可以更好地的实现这种效果;
4)虽然#define可以被这么多种东西替代,但是#define还是必不可少的。#ifndef/#ifdef更是对编译流控制的好方法。
总结:1)对于单纯变量,最好使用以const对象或enmu替换#define;
2)对于形似函数的宏,最好改用inline函数替换#define。
3、尽可能地使用const
1)防止意外修改不该被修改的内容,使之尽早在编译时暴露出来。Const可以修饰任何对象,函数参数,函数返回类型,成员函数本体;
2)对于成员函数,如果不希望其改变对象,可以声明为const的;
3)const版本和non-const版本的成员函数实现等价时,可以令non-const版本的调用const版本的,使用const_cast去除const即可。
4、确保对象在使用前已先被初始化
1)永远在使用对象和变量之前先将它初始化;
2)确保每一个构造函数都将对象的每一个成员初始化,尽量使用初始化列表进行初始化,并且最好依照声明次序初始化;
3)跨编译单元的初始化顺序,以local static对象替换non-local-static对象。
5、了解C++默默编写并调用哪些函数
1)如果我们什么都不写的话,编译器也会为我们生成默认构造函数,析构函数,拷贝构造函数,以及一个赋值构造函数;
2)如果我们自己声明了这些函数,那么C++会使用我们自己编写的这些函数,不会再使用那些默认的;
3)编译器为我们生成的默认构造函数是没有参数的,如果我们声明了其他的有参数的构造函数,那么这个默认构造函数也会消失,我们如果仍想要无参数的构造函数,那么就要自己再声明一个。
4)编译器为我们生成的析构函数,不会是virtual的,除非这个类的基类有virtual析构函数,基类析构尽量需写成virtual。
6、若明确不想使用编辑器自动生成的函数,就该明确拒绝
1)如果我们不容许拷贝或者赋值一个对象,写一个private的拷贝构造函数和赋值构造函数是一个好办法,这样从类外就不能调用了,但是从类内还是可以调用的,这时候,我们可以只声明,而不定义,这样,即使有人不小心使用了,在编译链接的时候,也会报错,把错误提前暴露,才容易发现。
2)如果我们怕忘记,或者为了方便,也可以定义一个基类uncopyable,让我们的类继承这个类即可, boost中noncopyable就是这个作用,另外这也是单例模式的基本原理。
7.为多态基类声明virtual析构函数
1)如果我们不这样做,当使用基类指针控制子类对象时,当析构时会发生只析构基类部分,而子类部分不被释放的情况,从而导致内存泄露;
2)只要这个类中有一个其他的虚函数,那么就使用虚析构函数,即这个类可能作为基类或者接口时。
3)如果基类不是虚析构函数,那我们就不要去继承这个类,不然会引出一大串麻烦;
4)析构函数调用的顺序:基类的析构函数先调用,然后依次向下调用子类的析构函数。
8.别让异常逃离析构函数
1)析构函数绝对不要抛出异常,如果可能抛出异常,析构函数应该捕获异常,不处理或者停止程序。
9.绝不在构造和析构函数中调用virtual函数
1)我们知道,如果有继承,构造函数会先调用父类构造函数,而如果构造函数中有虚函数,此时子类还没有构造,所以此时的对象还是父类的,不会触发多态。因为在基类构造期间,virtual函数不是virtual函数;
2)如果要在构造函数中执行virtual函数,可由派生类将必要的构造信息传递给基类构造函数。
10.令operator=返回一个reference to *this
1)所有内置类型和标准程序库提供的类型都这样遵守。
11.在operator= 中处理“自我赋值”
1)自我赋值会导致程序出错的情况在于,如果字段中有指针,先删除指针原来指向的对象,指向等号右边的对象。而自我赋值时会导致原来的对象被删除,而等号右边的对象也被删除,因而会出错。
2)解决的方法其实最简单的就是先判定是否相等,如果和自己是同一个对象,直接返回*this即可。
12.复制一个对象时勿忘其每一个成分
1)当我们使用拷贝函数的时候,应当确保复制对象内的所有成员变量以及从父类继承的成分;
2)不用使用拷贝函数来实现另外一个拷贝函数,应当提取公共部分,进行函数编写。