【C++笔记】《Effective C++》笔记

《Effective C++ 改善程序与设计的55个具体做法》笔记

一、让自己习惯C++

 

条款01:视C++为一个语言联邦

1.     C++由四个次语言组成:

2.     C语言

3.     Object-Oriented C++ 面向对象C++

4.     Template C++ 模板C++ C++的泛型编程部分

5.     STL 标准模板库

 

C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。

 

条款02:尽量以constenuminline、替换#define

const, enum, inline 替换#define主要是两个维度。 一个是数据,一个是函数。

要注意一种情况:累的成员常量在类中应该在声明时加上static。

使用inline代替宏定义,这一点主要是说的函数宏定义。使用宏定义函数时,好处是省去函数调用的开销,缺点也很明显,比如定义结构不清晰,参数求值的副作用。

a.对于单纯常量,最好以const对象或者enums替换#defines;

b.对于形似函数的宏(macros),最好改用inline函数替换#defines;

 

条款03:尽可能使用const

如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自己是常量;如果两边都有,则被指物和指针两者都是常量。

const可以用来修饰 指针 / 迭代器 / 引用 / 参数 / 返回值 / 函数类型。

尽可能的使用const可以借助编译器检查出一些编译阶段的错误。

当const成员函数和非const成员函数有着实质等价的实现时,用非const版本调用const版本可以避免代码重复。

编译器强制实时【二进制常量】,例如const成员变量不会允许被赋值。 而【逻辑上的常量】需要自己根据实际情况来决定。

 

a.将某些东西声明为const可帮助编译器侦测出错误用法。const可以被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。

b.编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)。

c.当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可以避免代码重复。

 

条款04:确定对象被食用前已先被初始化

       内置类型成员变量的初始化、类对象成员的成员变量的初始化等,建议都使用初始化列表进行初始化,而非赋值。

       在舒适化列表的初始化顺序:基类优先、其他成员按照在类中声明的顺序。

 

a.为内置型对象进行手工初始化,因为C++不保证初始化它们。

b.构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。

c.为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。

 

 

 

二、构造、析构、赋值运算

 

条款05:了解C++默默编写并调试哪些函数

带引用成员的类,如果不为其主动编写operator=,则对象之间的赋值操作是不能编译通过的;

带const成员的类,如果不为其编写operator=,则对象之间的赋值是不能通过编译的。

      

编译器可以暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数。

 

 

条款06:若不想使用编译器自动生成的函数,应该明确拒绝

 

根据条款05我们知道编译器会为类(在一定条件下)创建默认的构造、析构、拷贝构造、operator=函数,而当我们明确不需要编译器自作多情时,就该明确告诉编译器:别费劲了,老子有主了~   即把这些函数声明(可以只声明而不定义)为private的。 

当然这种方法并不保险,因为成员函数和friend函数仍然可以访问到这些函数,我们可以谨慎的不在成员函数和friend函数中对其进行调用(这一点,当只声明而不定义这些函数的时候,linker会帮助我们把不期望的调用找出来)。

另一种方法:

定义一个基类,该基类的作用是把不想让编译器自动生成的函数在该基类中声明为private的,然后让其他类继承该基类。

当客户端在我们的类没有主动定义一个这样的函数的时候去调用这些函数事,则编译器会自动生成对应的函数,而其【编译器生成版本】会尝试调用基类的对应兄弟而就会被决绝(private)。

 

为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的base class也是一种做法。

 

 

 条款07:为多台基类声明virtual析构函数

    任何一本C++语法教材上都会讲这一点(如果没讲,扔掉它),这么做到原因是可以让deletepBase操作能够正确的执行子类的析构函数。

需要说明的是当一个类不是用来当作基类或者不是用来在多态场景下使用时,就不要为这个类的析构函数声明为virtual的,因为这会额外给这类加大体积。

 

a.带多态性质的base class应该声明一个virtual析构函数,如果class带有任何virtual函数,他应该拥有体格virtual析构函数。

b.Classes的设计目的如果不是作为base class使用,或者不是为了具备多态性,就不应该声明virtual析构函数。

 

 

 

 

 

条款08:别让异常逃离析构函数

       a.别让析构函数中产生异常,如果非要产生则要么吞下(不传播),要么结束程序。如果某个操作可能会抛出异常,则应该让其在一个普通函数中执行,而非在析构函数中执行。

       b.如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(不应该是在析构函数)执行该操作。

 

条款09:绝不在构造和析构函数中调用virtual函数

【在基类构造期间,virtual函数不是virtual函数】,即如果在基类的构造函数中调用了virtual函数,则虽然该virtual函数在子类中不同的实现,则在基类的构造函数中调用的却是基类的virtual函数,如果该virtual函数在基类中没有实现则linkier会给出错误从而让我们有章可循;当如果该virtual虚函数在基类中有一个实现时,则该错误就很难被发现。

析构函数同理。


在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class。



。。。。。。。。。。。。。

猜你喜欢

转载自blog.csdn.net/hellozex/article/details/80840630
今日推荐