C++是一个由相关语言组成的联邦而非单一语言。其中包括:
记住:C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。
#define MAX 9999
记号名称MAX可能没==进入记号表==,于是当你运用此常量但获得编译错误信息时,可能呢过会因为看到9999而不是MAX而无法准确追踪错误。而解决之道是以一个常量替换上述的宏。
const int Max = 9999;
作为一个语言常量,Max一定会被编译器看到,并进入记号表。
此外还需要注意两个特别情况:
static const int numTurns = 5;
这是一个声明式而非定义式。通常C++要求你对你使用的任何东西提供一个定义式,但如果它是class的专属常量又是static整型,则需特别处理。只要不取他们的地址,你可以声明并使用他们而无需提供定义式。
const int Namespace::numTurns;
这是一个定义式,应该把它放在实现问题中,由于声明时已经获得初值,定义时不再设初值。
The enum hack 补偿方法:一个属于枚举类型的数值可以权充int被使用。并且不能被取地址。
记住:
对于单纯常量,最好以consy对象或enums替换#defines。
对于形似函数的宏,最好改用inline函数替换#defines。
如果const出现在星号左边,表示被指物是常量,如果出现在星号右边,表示指针自身是常量,如果出现在两边则都是。
将const实施于成员函数的目的,是为了确认该成员函数可作用域const对象身上。这一类成员函数之所以重要,基于以下两个理由:
class TextBlock {
public:
...
const char& operation [] (std::size_t position) const {
return text[position];
} // operator [] for const object.
char& operation [] (std::size_t position) {
return text[position];
} // operator [] for non-const object.
把一个成员函数定义为const,意味着对他返回值不能操作,但只要返回一个by value就是实现这个功能,那么成员函数是congst究竟意味什么?
这里有两个流行概念:
此阵营的人认为,成员函数只有在不更改对象之任何成员变量(static除外)时才可以说是const,也就是说不能更改对象内热任何一个bit,也就是说它不更改对象内任何一个bit!(编译器只要检查赋值行为即可)
然而,许多成员函数虽然不十分具备const性质却能通过bitwise测试。更具体地说,一个更改了“指针所指物”的成员函数虽然不能算是const,但如果只有指针(而非所指指物)隶属于对象,那么此函数为bitwise const不会引发编译器异议。
class CText {
public:
...
char& operator [] (std::size_t position) const {
return text[position];
}
// 不适当,把函数声明为const却返回一个reference纸箱对象内部值。
private:
char* text;
}
const CText cctb("Hello");
char* pc = &cctb[0];
*pt = "J"; // successive! cctb : "Jello" now.
此情况导出所谓logical constness。
一个const成员可以修改它所处理的对象内的某些bit,但只有在客户端侦测不出来时才可以。
利用mutable就可以释放掉non-static成员变量的bitwise constness约束。
class text {
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 Text&>(*this)[position]);
{
值得注意的是,方向操作–令const版本调用non-const版本以避免错误,是不该做的。因为,const成员函数承诺绝不改变其对象的而逻辑状态,non-const成员函数却没有这般承诺。如果在const函数内调用non-const函数,对象可能会被改变。
请记住:
在构造函数中最好使用初始化列表,因为在初始化列表中才是真正的初始化,效率更高。另外在初始化列表中还要注意次序的问题,一般来说是按照成员变量的声明次序来初始化变量的。
所谓static对象,其寿命从被构造出来知道程序结束为止,因此stack和heap-based对象都被删除。在函数内的的static对象称为local-static对象(因为他们对函数而言是local),其他static对象称为non-local static对象。程序结束时static对象会被自动销毁,也就是他们的析构函数会在main()结束时被自动调用。
所谓编译单元是指产出单一目标文件的那些源码。基本上它是单一源码文件加上其所含入的头文件。
现在,我们关系的问题是,对于两个以上源码文件,每一都含有至少一个non-local static对象。真正的问题是:如果某编译单元内的某个non-local static对象的初始化动作使用了另一编译单元内的某个non-local static对象,它所用到的这个对象可能尚未被初始化,因为C++对“定义不同编译单元的non-local static对象”的初始化次序没有明确定义。
实例可以帮助理解:
class FileSystem {
public:
...
std::size_t numDisk() const;
..
};
extern static FileSystem tfs;
class Directory {
public:
Directory(params) {
...
std::size_t disks = tfs.numDisks();
// error occurs!
...
}
};
问题发生了!你不知道disk在调用tfs之前,tfs有没有被初始化!!!
C++对“定义域不同编译单元内的non-local static对象”的初始化次序没有明确定义!因为这是没有必要的。
幸运的是,我们可以使用Singleton模式来解决这个问题。这个手法的基础在于:C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇到该对象之定义式”时被初始化。所以如果你以“函数调用”(返回一个reference指向local static对象)替换“直接访问non-local static对象”,你就可以保证reference指向一个历经初始化的对象。
class FileSystem {...}; // just the same as before.
FileSystem& tfs() {
static FileSystem fs;
return fs;
}
class Directory {
public:
Directory(params) {
...
std::size_t disks = tfs().numDisks();
// error occurs!
...
}
};
Directory& tempDir() { // 这个函数用来替换tempDir对象。
static Directory td;
return td;
}
请记住: