《Effective C++》读书笔记
2007-03-18 01:12
232 查看
条款1:视C++为一个语言联邦
可分为四个:
1. C
2. Object-Oriented C++
3. Template C++
4. STL
条款2:尽量以const,enum,inline替换#define
常量定义式通常被放在头文件内
Class专属常量,为了将常量的作用域限制在类内,必须让它成为Class的一个成员;而为确保此常量至多只有一份实体,必须让它成为一个static成员。
class GamePlayer{
private:
static const int NumTurns = 5; //常量声明式
int scores[NumTurns]; //使用该常量
…
};
必须另外提供定义式如下:
const int GamePlayer::NumTurns; // NumTurns的定义;
这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值,因此定义时不可以再设初值。
Remember:
l 对于单纯常量,最好以const对象或enums替换#defines
l 对于形似函数的宏(macros),最好改用inline函数替换#defines
条款3:尽可能使用const
另函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性:
class Rational {…};
const Rational operator* (const Rational& lhs, const Rational& rhs);
Rational a, b, c;
…
(a * b) = c; // 在a * b的成果上调用operator=
const成员函数
1. 使class接口比较容易理解。这是因为,得知哪个函数可以改动对象内容而哪个函数不行。
2. 它们使“操作const对象”成为可能。
两个成员函数如果只是常量性不同,可以被重载。
在const成员函数内不能给非mutable成员变量赋值。const成员函数中的(*this)对象是const的。
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
条款4:确定对象被使用前先已被初始化
l 为内置型对象进行手工初始化,因为C++不保证初始化它们。
l 构造函数最好使用成员初始值列表进行初始化(只调用一次成员变量的copy构造函数),而不是在构造函数本体内使用赋值操作(先调用default构造函数然后再调用copy assignment操作符)。初值列列出的成员变量,其排列次序应该和它们在class中声明次序相同。
l 如果成员变量是const或references,就一定需要初始值(成员初始值列表来初始化),不能被赋值。
l static对象是其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象被排除。这种对象包括global对象、定义于namespace作用域内的对象、在classes内、在函数内、以及在file作用域内被声明为static对象。
函数内的static对象称为local static对象,其他static对象称为non-local static对象。
l 为免除“跨编译单元之初始化次序”,以local static对象替换non-local static对象。(利用设计模式中的singleton模式实现,C++保证函数内的local static对象会在“该函数被调用期间”“首次遇上该对象定义式”时被初始化。所以如果以“函数调用”<返回一个reference指向local static对象>替换“直接访问non-local static对象”,就获得了初始化的保证。)
条款5:了解C++默默编写并调用哪些函数
对于内含reference成员或const成员的classes,如果要支持赋值操作,必须自己定义copy assignment操作符。
条款6:若不想使用编译器自动生成的函数,就该明确拒绝
为驳回编译器自动(暗自)提供的功能(如自动提供copy复制函数和copy assignment函数),可将相应的成员函数声明为private并且不予实现。使用象Uncopyable这样的base class也是一种做法。
条款7: 为多态基类声明virtual析构函数
条款8:别让异常逃离析构函数
l 析构函数绝对不要突出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。
l 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。
条款34:区分接口继承和实现继承
Class设计继承关系时可以区分为函数接口继承和函数实现继承;
其差异为:
1. Pure virtual 函数
Derived classes只继承成员函数的接口
2. Impure virtual 函数
Derived classes 同时继承函数的接口和实现,又能够override它们所继承的实现
3. Non virtual 函数
Derived classes 同时继承函数的接口和实现,并且不允许覆写任何东西
Pure virtual 函数的特性:
1. 必须被任何“继承了它们”的具体class重新声明
2. 在抽象class中通常没有定义(也可以为pure virtual函数提供定义,但调用它的唯一途径是“调用时明确指出class名称”。)
允许impure virtual函数同时指定函数声明和函数缺省行为,却有可能造成危险。避免危险,可以切断”virtual函数接口”和其“缺省实现”之间的连接。
两种方法:
1. 将impure virtual 函数改为一个pure virtual 函数,在其函数中inline调用具有缺省行为的独立函数(分为两个函数实现)
2. pure virtual 函数必须在derived class 中声明,但他们也可以拥有自己的实现(一个函数实现)
Remember:
l 接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口
l pure virtual函数只是具体指定接口继承
l impure virtual具体指定接口继承及缺省实现继承
l non-virtual函数具体指定接口继承以及强制性实现继承
待续。。。
可分为四个:
1. C
2. Object-Oriented C++
3. Template C++
4. STL
条款2:尽量以const,enum,inline替换#define
常量定义式通常被放在头文件内
Class专属常量,为了将常量的作用域限制在类内,必须让它成为Class的一个成员;而为确保此常量至多只有一份实体,必须让它成为一个static成员。
class GamePlayer{
private:
static const int NumTurns = 5; //常量声明式
int scores[NumTurns]; //使用该常量
…
};
必须另外提供定义式如下:
const int GamePlayer::NumTurns; // NumTurns的定义;
这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值,因此定义时不可以再设初值。
Remember:
l 对于单纯常量,最好以const对象或enums替换#defines
l 对于形似函数的宏(macros),最好改用inline函数替换#defines
条款3:尽可能使用const
另函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性:
class Rational {…};
const Rational operator* (const Rational& lhs, const Rational& rhs);
Rational a, b, c;
…
(a * b) = c; // 在a * b的成果上调用operator=
const成员函数
1. 使class接口比较容易理解。这是因为,得知哪个函数可以改动对象内容而哪个函数不行。
2. 它们使“操作const对象”成为可能。
两个成员函数如果只是常量性不同,可以被重载。
在const成员函数内不能给非mutable成员变量赋值。const成员函数中的(*this)对象是const的。
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
条款4:确定对象被使用前先已被初始化
l 为内置型对象进行手工初始化,因为C++不保证初始化它们。
l 构造函数最好使用成员初始值列表进行初始化(只调用一次成员变量的copy构造函数),而不是在构造函数本体内使用赋值操作(先调用default构造函数然后再调用copy assignment操作符)。初值列列出的成员变量,其排列次序应该和它们在class中声明次序相同。
l 如果成员变量是const或references,就一定需要初始值(成员初始值列表来初始化),不能被赋值。
l static对象是其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象被排除。这种对象包括global对象、定义于namespace作用域内的对象、在classes内、在函数内、以及在file作用域内被声明为static对象。
函数内的static对象称为local static对象,其他static对象称为non-local static对象。
l 为免除“跨编译单元之初始化次序”,以local static对象替换non-local static对象。(利用设计模式中的singleton模式实现,C++保证函数内的local static对象会在“该函数被调用期间”“首次遇上该对象定义式”时被初始化。所以如果以“函数调用”<返回一个reference指向local static对象>替换“直接访问non-local static对象”,就获得了初始化的保证。)
条款5:了解C++默默编写并调用哪些函数
对于内含reference成员或const成员的classes,如果要支持赋值操作,必须自己定义copy assignment操作符。
条款6:若不想使用编译器自动生成的函数,就该明确拒绝
为驳回编译器自动(暗自)提供的功能(如自动提供copy复制函数和copy assignment函数),可将相应的成员函数声明为private并且不予实现。使用象Uncopyable这样的base class也是一种做法。
条款7: 为多态基类声明virtual析构函数
条款8:别让异常逃离析构函数
l 析构函数绝对不要突出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。
l 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。
条款34:区分接口继承和实现继承
Class设计继承关系时可以区分为函数接口继承和函数实现继承;
其差异为:
1. Pure virtual 函数
Derived classes只继承成员函数的接口
2. Impure virtual 函数
Derived classes 同时继承函数的接口和实现,又能够override它们所继承的实现
3. Non virtual 函数
Derived classes 同时继承函数的接口和实现,并且不允许覆写任何东西
Pure virtual 函数的特性:
1. 必须被任何“继承了它们”的具体class重新声明
2. 在抽象class中通常没有定义(也可以为pure virtual函数提供定义,但调用它的唯一途径是“调用时明确指出class名称”。)
允许impure virtual函数同时指定函数声明和函数缺省行为,却有可能造成危险。避免危险,可以切断”virtual函数接口”和其“缺省实现”之间的连接。
两种方法:
1. 将impure virtual 函数改为一个pure virtual 函数,在其函数中inline调用具有缺省行为的独立函数(分为两个函数实现)
2. pure virtual 函数必须在derived class 中声明,但他们也可以拥有自己的实现(一个函数实现)
Remember:
l 接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口
l pure virtual函数只是具体指定接口继承
l impure virtual具体指定接口继承及缺省实现继承
l non-virtual函数具体指定接口继承以及强制性实现继承
待续。。。
相关文章推荐
- Effective C++ 读书笔记(35-44):继承关系与面向对象设计
- 【C++】析构函数和virtual函数引发的隐晦问题 ——《Effective C++》读书笔记5
- 《Effective C++(第三版)》读书笔记
- Effective C++读书笔记之四:确定对象被使用前已先被初始化
- 《effective C++》读书笔记三——资源管理
- [C/C++]《Effective C++》读书笔记
- Effective C++ 读书笔记——尽量以const,enum,inline替换 #define
- Effective C++ 读书笔记(13)
- Effective C++ 读书笔记(14)
- Effective C++ 读书笔记(21)
- Effective C++ 读书笔记(24)
- Effective C++ 读书笔记(25)
- Effective C++ 部分读书笔记
- 《Effective C++》读书笔记(二) 构造/析构/赋值运算 (第一部分)
- 【读书笔记】Effective C++-1 让自己习惯C++(之三)
- Effective C++ --读书笔记之一
- Effective C++ 读书笔记之implemenations(3)
- Effective C++ 读书笔记之Part1.Accustoming Yourself to C++
- Effective C++ 读书笔记之Part3.Resource Management
- Effective C++ 读书笔记二