Effective C++ 1.让自己习惯C++

时间:2021-02-07 22:34:10
//条款01:视C++为一个语言联邦
// 1:C++主要包含的语言为:
// A:C。说到底C++仍然以C为基础。区块(blocks)、语句、预处理器、内置数据类型、数组、指针等均来自于C。许多时候C++对于问题的解决不过是较高级的C解法。当以C++内的C成分
// 工作的时候,高效编程守则映照出C语言的局限:没有模板,没有异常,没有重载等。
// B:Object-Oriented C++。这部分也就是C with Class所述求的:class(包括构造函数和析构函数),封装,继承,多态,虚函数(动态绑定)等。这一部分是面向对象设计之古典守则在C++上最直接实施。
// C:Template C++。这是C++的泛型编程。
// D:STL。它对容器、迭代器、算法以及函数对象的规约有极佳的紧密配合与协调。
// 2.C++并不是一个带有一组守则的一体语言,它是四个次语言组成的,每个次语言都有自己的规约。 //条款02:尽量以const enum inline替换#define
// 1.const 与 #define
// A:#define CVALUE 5 上述语句并没有使得CVALUE被编译器看见,有可能就没有进入记号表内。于是当使用此常量导致的编译错误可能会提及5而不提及CVALUE,这将使问题难以追踪。
// 并且由于预处理器盲目地将宏名称替换为其对应的东西而导致代码产生冗余。而下列语句 const int CValue = 5; 则不会出现上述的问题。
// B:当在类中使用类的专属常量的时候可以使用const来实现,不能用#define实现,因为#define不注重作用域,一旦宏被定义,它就在其后的编译过程中有效(除非在某处被#undef)
class CTest
{
public:
const static int CSValue0 = 1; //类的专属常量的声明。在vs2010中不需要再类外进行声明就可以直接使用此变量
const static int CSValue1; //类的专属常量的声明,但是没有进行定义,在使用前必须进行定义,否则会出错
};
const static int CSValue0; //正确,类的专属常量的定义。
const int CTest::CSValue1 = 1; //类的专属常量的定义。
// 2.enum 和 #define
// A:在class编译期间需要一个class常量值也可以使用类内枚举。对一个const变量取地址是合法的,对一个枚举值取地址是非法的,而取一个#define的地址通常也是不合法的
// B:优秀的编译器不会为整形const对象设定另外的存储空间(除非创建一个指针或引用指向该对象),不过不够优秀的编译器(包括vs2010)却可能为const对象创建存储空间。但是enum和#define都绝不会导致非必要的内存分配
class CTest
{
public:
CTest() : CCV(10) {} #define CDV 10
enum{CEV};
const int CCV;
};
int size = sizeof(CTest); //vs2010下, size值为4
// 3.inline 和 #define
// A:宏看起来像函数,但是不会招致函数调用带来的额外开销。比如:
#define Max(a, b) ((a) > (b) ? (a) : (b)) //首先这里必须为每个变量加上括号
int a = 10, b = 10;
int value0 = Max(++a, b); //value0 = 12 其中a被累加2次
int value1 = Max(++a, b + 10); //value1 = 20 其中a被累加1次
// B:由上述代码可知,a被累加的次数居然取决于被比较的对象,这就十分荒谬了。而使用inline则可以完美解决上述问题
template<typename T>
inline T Fun(const T &a, const T &b)
{
return a > b ? a : b;
}
// 4.有了const enum inline 我们对预处理器(特别是#define)的需求降低了,但是并非完全消除。#include仍然是必需品。#ifdef/#ifndef也扮演控制编译的重要角色。 //条款03:尽可能的使用const
// 1.令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性。比如:
const operator *(const CTest &Test1, const CTest &Test2); //可以有效防止比如: (Test0 * Test1) = Test2;
// 2.将不需要被改变的量声明为const的,有助于在判断表达式中意外将==写成=造成的错误,以及防止其被意外修改。
// 3.将const作用于成员函数的目的是为了确认该成员函数可作用于const对象上。
// 4.区分顶层const和底层const,顶层const表示const所修饰的变量本身是常量,而底层const一般作用于指针,表示指针所指对象是常量。
// 5.将成员变量声明为mutable的,则即使在常量成员函数中也能修改这个变量的值。
// 6.当成员函数的常量版本和其非常量版本有着实质的等价实现的时候,最好是在其非常量版本中调用常量的版本,反之则违背了常量成员函数不修改其成员的规则,可能会导致逻辑错误。 //条款04:确定对象被使用前已经被初始化
// 1.读取未初始化的值会导致不明确的行为。
// 2.C++对于定义在不同cpp中的全局变量的初始化顺序没有进行规定,所以一个cpp中的全局变量不要依靠另一个cpp中的全局变量进行初始化,否则可能会产生bug
// 3.对于第二点的解决方法是:将全局变量局部static化。比如:
int& fun()
{
static int value = 10;
return value;
}//将每个全局变量搬到自己的专属函数中,然后由用户调用这些函数以获取其需要的变量。C++保证,函数内的局部static变量会在该函数调用期间,首次遇上该对象的定义式时被初始化(当此函数没有被调用的时候就省去了此变量的构造和析构)。
// 4.构造函数最好使用成员初始化列表进行对其成员的初始化。