1)视C++为一个语言联邦:
1-以C为基础:区块、语句、预处理器、内置数据类型、数组、指针等等。
2-C++ C With Classes:classes(包括构造函数和析构函数),封装、继承、多态。
3-C++ 泛型编程:Template设计编程
4-STL:STL是一个template库,对容器、迭代器、算法以及函数对象的规约协调。
2)尽量用const/inline/enum替换#define
CC:对于单纯常量,最好以const对象或enums替换#defines。
对于形似函数的宏,最好改用inline函数替换#defines。
3)尽可能使用const
CC:将某些东西声明为const可帮助编译器侦测出错误用法。
const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体
4)确定对象使用前已先被初始化
CC:构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。
5)了解C++默默编写并调用哪写函数
1-函数默认:构造、析构、拷贝
2-递归函数: 递归条件(指的是函数调用自己), 基线条件(函数不再调用自己)
3-D&C方法:找出基线条件(很可能是空数组或只包含一个元素的数组)、不断将问题分解(或缩小规模),直到符合基线条件
4-栈两种操作:压入(插入)、弹出(删除并读书)
5-函数:提供定义函数,提供函数原型,调用函数
6)为多态基类声明virtual析构函数
CC:带多态性质的基类应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该有一个virtual析构函数。
7)别让异常逃离析构函数
CC:析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获任何异常,然后吞下它们(不传播)或结束程序。
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数执行该操作。
8)绝不在构造和析构函数过程中调用virtual函数
CC:在构造和析构函数期间不要调用virtual函数,因为这类调用从不下降至derived class(派生类)。
9)令operator=返回一个reference to(引用)指针
CC:令operator=(const typeName & rhs)返回一个reference to *this。
10)在operator=中处理"自我赋值"
CC:确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。
11)复制对象时勿忘其每一个成分
CC:复制函数应该确保复制“对象内的所有成员变量”及“所有基类成分”。
不要尝试以某个复制函数实现另一个复制函数。应该将共同机能放进第三个函数中,并由两个复制函数共同调用。
12)以对象管理资源/“资源取得时机便是初始化时机”(RAll)
CC:所谓资源就是,一旦用了它,将来必须还给系统。
~获得资源后立刻放进管理对象内。
~管理对象运用析构函数确保资源被释放。
为防止资源泄漏,请使用RAll对象,他们在构造函数中获得资源并在析构函数中释放资源。
两个常被使用的RAll 类分别是trl::shared_ptr和auto_ptr。前者通常是较佳选择,因为其复制行为比较直观。若选择auto_ptr,复制动作会使它指向null.
13)在资源管理类中小心复制行为
CC:当一个RAll对象被复制时,则要么禁止复制(并不合理),要么对底层资源祭出“引用计数法”(有时希望保有资源,直到它的最后一个使用者(某对象)被销毁。
复制RAll对象必须一并复制它所管理的资源,所以资源的复制行为规定RAll对象的复制行为。
普遍而常见的RAll 类的复制行为是:抑制复制、施行引用计数法。但其他行为也可能都被实现。
14)在资源管理类中提供对原始资源的访问
APIs(应用程序)往往要求访问原始资源,所以每一个RAll类应该提供一个“取得其所管理之资源”的办法。
对原始资源的访问可能经由显示转换或隐式转换。一般而言显示转换比较安全,但隐式转换对客户比较方便。
15)成对使用new和delete时要采取相同形式
CC:如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。
16)以独立语句将newed对象置入智能指针
CC:以独立语句将newed对象储存(置入)智能指针内。如果不这样做,一旦异常被抛出,又可能导致难以察觉的资源泄漏。
17) 让接口容易被正确使用,不易被误用
struct Day{
explicit Day(int d)
:val(d){}
int val;
};
struct Month{
explicit Month(int m)
:val(m){}
int val;
};
struct Year{
explicit Year(int y)
:val(y){}
int val;
};
class Date{
public:
Date(const Month & m,const Day & d,const Year & y);
...
};
Date d(30,3,1995); //错误!不正确的类型
Date d(Day(30),Month(3),Year(1995)); //错误!不正确的类型
Date d(Month(3),Day(30),Year(1995)); //ok,类型正确
CC:“促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容。
“阻止误用”的办法包括建立新类型、限制类型的上的操作,束缚对象值,以及消除客户的资源管理责任。
trl::shared_ptr支持定制型删除器。可防范DLL问题,可被用来自动解除互斥锁等。
18)设计class犹如设计type
CC:新type的对象应该如何被创建和销毁?
对象的初始化和对象的赋值该有什么样的差别?
新type的对象如果被passed by value,意味着什么?
什么是新type的“合法值”?
你的新type需要配合某个继承图系吗?
你的新type需要什么样的转换?
什么样的操作符和函数对此新type而言是合理的?
什么样的标准函数应该驳回?
谁该取用新type的成员?
什么是新type的“为声明接口”?
你的新type有多么一般化?
你真的需要一个新type吗?
Class的设计就是type的设计。在定义一个新type之前,请确定你已经考虑过以上所述。
19)宁以pass-by-reference-to-const替换pass-by-value
CC:尽量以宁以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题。
以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。
20)必须返回对象时,别妄想返回其reference
CC:绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。
21)将成员变量声明为private
CC:切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供Class作者以充分的实现弹性。
proteced并不比public更具封装性。
22)宁以non-member、non-friend替换member函数
CC:宁以non-member、non-friend替换member函数。这样做可以增加封装性、包裹弹性和机能扩充性。
23)若所有参数皆需类型转换,请为此采用non-member函数
CC:如果你需要为某个函数的所有参数(包括this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。
24)考虑写出一个不抛异常的swap函数
CC:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes(而非templates),也请特化std::swap.
调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。
为用户定义类型进行std:: templates全特化式是好的。但千万不要尝试在std内加入某些对std而言全新的东西。
25)尽可能延后变量定义式的出现时间
CC:尽可能延后变量定义式的出现时间。这样做可增加程序的清晰度并改善效率。