背景:
C++的难学,不仅在其广博的语法,以及语法背后的语义,以及语义背后的深层思维,以及深层思维背后的对象模型;C++的难学还在于它提供了四种不同而又相辅相成的编程范型 (programming paradigms):procedural-based,object-based,object-oriented,generics(泛型形式)。
用途:
这本书的最佳用途就是彻底了解C++如何行为、为什么那样行为,以及如何运用其行为形成优势。盲目应用书中条款是非常不合适的,但如果没有好理由,你或许也不应该违反任何一个条款。
术语:
声明式(对象声明式、函数声明式,类声明式、模板声明式)。
定义式的任务是提供编译器一些声明式所遗漏的细节,对对象而言,定义式是编译器为此对象拨发内存的地点;对函数或者函数模板而言,定义式提供了代码本体;对类或者类模板而言,定义式列出它们的成员。
初始化是“给予对象初值”的过程。对用户自定义类型的对象而言,初始化由构造函数执行。
STL(Standard Template Library)是C++标准程序库的一部分。包括容器、迭代器和算法。
接口=函数的签名
C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。
有了const、enum和inline,我们对预处理(特别是#define)的需求降低了,但并非完全消除。#include仍然是必需品,而#ifdef/#ifndef也继续扮演控制编译的重要角色。
1.对于单纯常量,最好以const对象或enums替换#defines.
2.对于形似函数的宏,最好改用inline函数替换#define.
const的一件奇妙事情是,它允许你指定一个语义约束,也就是指定一个“不该被改动”的对象,而编译器会强制实施这项约束。它允许你告诉编译器和其他程序员某值应该保持不变。只要这(某值保持不变)是事实,你就应该确实说出来。
1.理解:令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。
2.const成员函数:将const实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上。这一类成员函数之所以重要,基于两个理由:其一,它们使class接口比较容易被理解;其二,它们使“操作const对象”成为可能。这对编写高效代码是个关键,因为改善C++程序效率的一个根本方法是以pass by reference-to-const方式传递对象。
/*两个成员函数如果只是常量性不同,可以被重载,以下代码用来说明这一点:*/
/*功能类:用来表现一大块文字*/
class TextBlock{
public:
……
const char& operator[](std::size_t position) const{return text[position];}
char& operator[](std::size_t position) const{return text[position];}
private:
std::string text;
}
/*调用*/
TextBlock tb("Hello");
std::cout << tb[0]; //调用non-const TextBlock::operator[]
void print(const TextBlock& ctb)
{
std::cout << ctb[0]; //调用const TextBlock::operator[]
……
}
/*只要重载operator[]并对不同的版本给予不同的返回类型,就可以令const和non-const TextBlock&获得不同的处理:*/
std::cout << tb[0]; //OK,读一个non-const TextBlock
tb[0] = 'x'; //OK,写一个non-const TextBlock,tb的operator[]返回类型是reference to char
std::cout << ctb[0]; //OK,读一个const TextBlock
ctb[0] = 'x; //error:写一个const TextBlock,错误原因:该赋值企图对一个“由const版之operator[]返回”的const char&施行赋值动作。
3.成员函数如果是const意味着什么?以下两种方式中编译器强制实施bitwise constness,但编写程序时应该使用“概念上的常量性”(conceptual constness)
bitwise constness(physical contness) :成员函数只有在不更改对象之任何成员变量(static除外)时才可以说是const.这正是C++对常量性的定义。不幸的是许多成员函数虽然不十足具备const性质却能通过bitwise测试。
logical constness:上一种方式中不幸通过bitwise测试的情况引出了此种方式,也就是另一种理解。一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才可以如此。
4.const是个奇妙且非比寻常的东西,在指针和迭代器身上;在指针、迭代器及reference指涉的对象身上;在函数参数和返回类型身上;在local变量身上;在成员函数身上。
5.const版本调用non-const版本:错误。曾经承诺不改动的那个对象被改动了。non-const版本调用const:正确。non-const成员函数本来就可以对其对象做任何操作。
1.区分赋值和初始化;
2.为内置对象进行手工初始化,因为C++不保证初始化它们;
3.构造函数最好使用成员初值列(member initialization list),而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排序次序应该和它们在class中的声明次序相同;
4.为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。
所谓static对象,其寿命从被构造出来直到程序结束为止,因此stack和head-based对象都被排除。这种对象包括global对象、定义域namespace作用域内的对象、在classes内、在函数内、以及在file作用域内被声明为static的对象。函数内的static对象称为local static对象(因为它们对函数而言是local),其他static对象称为non-local static对象。程序结束时static对象会被自动销毁,也就是他们的析构函数会在main()结束后自动调用。
这块的问题是:如果某编译单元内的某个non-local static对象的初始化动作使用了另一编译单元内的某个non-local static对象,它所用到的这个对象可能尚未被初始化,因为C++对“定义域不同编译单元内的non-local static对象”的初始化次序并无明确定义。