一:View C++ as a federation of languages; 视C++为一个语言联邦
将c++视为由相关语言组成的联邦而非单一语言。理解其相关的次语言。
1.C
2.object-oriented C++
3.Template C++
4.STL
c++高效编程守则视情况而变化,取决于你使用C++的哪一部分
二:Prefer consts,enums,and inlines to #defines 尽量以const, enum, inline 替换 #define
即 宁可以编译器代替预处理器。
#define ASPECT_RATIO 1.653
宏定义中的记号名称没有被写入记号表,如果由关于此的编译错误时不会提到该记号。
以常量替换上述的宏
const double AspectRatio = 1.653;:好处 使用常量可能比使用#define导致较小量的代码
注:定义常量指针
const char* const authorName = "Scott Meyers";
使用const std::string authorNmae("Scott Meyers");更好些
类常量 为了将此常量限制在类内并且确保此常量至多只有一份实体,必须声明成一个静态成员常量。
注:enum hack
1.取址一个enum不合法,如果不想让别人获得一个指针或者引用指向某个整数常量,enum可以完成这个约束。enums也不会导致非必要的内存分配。
2.enum hack是模板元编程的基础技术
注:用模板内联函数代替宏定义函数
总结:对于单纯常量,以const或者enums替换#defines
对于形似函数的宏,以inline函数替换#defines
三:Use const whenever possible 尽可能使用const
const如果出現在星号左边,表示被指物是常量,如果出现在星号右边,表示指针自身是常量。
对于迭代器,类似T*指针。
声明迭代器为const表明该迭代器不得指向不同的东西,但它所指向的东西的值是可以改动的。
如果希望迭代器所指的东西不可改动,需要const_iterator (STL模拟一个const T*指针)。
例子:
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();
*iter = 10;
++iter;//error
std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10;//error
++cIter;
对于函数,const返回值,参数等会避免一些无意义的赋值错误。
const 成员函数:
两个成员函数如果只是常量性不同,可以被重载。
例子:
class TextBlock
{
public:
const char& operator[](std::size_t position) const {
return text[position];
}
char& operator[](std::size_t position)
{
return text[position];
}
private:
std::string text;
};
class CTextBlock
{
public:
char& operator[](std::size_t position) const {
return pText[position];
}
private:
char* pText;
};
const CTextBlock cctb("Hello");
char* pc = &cctb[0];
*pc = 'J';
这样虽然调用了const成员函数 也可以改变类的一些bit;
注:可以利用mutable关键字 释放掉non-static成员变量的bitwise constness约束
使得const成员函数也可以修改被mutable修饰的变量。
注:在const和non-const成员函数中避免重复
利用转型去掉重复代码:
class TextBlock
{
public:
const char& operator[](std::size_t position) const {
return text[position];
}
char& operator[](std::size_t position)
{
return
const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
private:
std::string text;
};
与此相反,const成员函数调用non-const成员函数是一种错误行为。
总结:
将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体。
编译器强制实施bitwise constness,但你编写程序时应该使用概念上的常量性。
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
四:Make sure that objects are initialized before they're used. 确定对象被使用前已先被初始化。
永远在使用对象之前先将它初始化。
1.内置类型 手工初始化 略
2.非内置类型,初始化责任落在构造函数身上,确保每一个构造函数都将对象的每一个成员初始化。
注意赋值和初始化的区别,c++规定对象的成员变量的初始化动作发生在进入构造函数本体之前。
可以使用参数列表的方式初始化,代替构造函数赋值的操作。(通常效率更高)
如果成员变量是const或references就一定需要初值,不能被赋值。所以可以总使用成员初始列表减少复杂度。
c++有着十分固定的成员初始化次序。基类早于派生类,在成员初始化列表中的顺序决定被初始化的顺序。
3.不同编译单元内定义的non-local static对象
static对象包括global对象,定义于namespace作用域内的对象,在classes内,在函数内,以及在file作用域内被声明为static的对象。
函数内的static对象称为local static对象,其他static对象称为non-local static对象。
c++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确定义。
注:trick 将每个non-local static对象搬到自己的专属函数内(该对象在此对象内被声明为static)。这些函数返回一个引用指向它所含的对象。
non-local static对象被local static对象替换了,c++保证函数内的local static对象会在该函数被调用期间,首次遇到该对象定义式时被初始化。
好处,可以确定顺序,不调用时不会被构造或者析构。
总结:
为内置型对象进行手工初始化,因为c++不保证初始化它们。
构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
为免除“跨编译单元初始化次序”问题,以local static对象替换non-local static对象。