C++是个多重范型编程语言,同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式。视c++为一个语言联邦,包括以下四个部分:
STL. C++标准模板库,包括容器、迭代器、算法以及函数对象等。
请记住:
C++高效编程守则视状况而变化,取决于使用的是这四个部分中的哪一部分。
等价描述为宁可以编译器代替预处理器。
#define即宏定义在预处理阶段进行处理,通常情况下记号名称会进入记号表(symbol table),也有可能在编译器处理前就被预处理器移走了,没有进入记号表,这时就会编译出错。解决的办法使用常量来代替宏。
例子:const double AspecRatio = 1.653;
来代替#define ASPEC_RATIO 1.653;
特别注意两点:
- 指针常量与常量指针,特别地,使用const string
代替const char* const
;
- class专属常量:static const来修饰,静态常量:
例如:class GamePlayer{ static const int NumTurns = 5; //常量声明式};
或者使用常量类内声明+类外定义:
class GamePlayer{static const int NumTurns;};// 常量声明式
const int GamePlayer::NumTurns = 5;//常量定义式
另外无法使用宏定义来创建一个class 专属常量,因为宏定义并不重视作用域。宏定义只是简单嵌入,并不做类型检查,所以在定义宏时,请为宏的所有实参加上小括号。
例如:#define CALL_MAX(a, b) ((a) > (b) ? (a) : (b))
调用:
int a = 5, b = 0;
CALL_MAX(++a, b); //a被累加1次
CALL_MAX(++a, b + 10); //a被累加2次
这是不可预料的行为,因此使用内联函数来代替宏定义函数。
请记住:
对于单纯常量,最好使用const对象或enum替换#defines。
对于形似函数的宏,最好改用inline函数替换#defines。
const关键字告诉编译器某值应该保持不变,即为只读的。
- 常量指针与指针常量
例如:STL中的迭代器。
const vector<int>::iterator ite1;//类似于常量指针,ite1是只读的
vector<int>::const_iterator ite2;//类似于指针常量,*ite2是只读的
const Rational operator*(const Rational& lhs, const Rational& rhs);//有理数类乘法重载
Rational a, b, c;
if(a * b = c)...//这一步本来是想做比较操作,误写成了赋值操作,因为返回const对象,编译器就能发现这个错误。
const className const*
。 pass by reference-to-const
方式传递对象,这一前提是必须使用const成员函数来处理const对象。 const char& operator[](int pos) const;//const版本
char& operator[](int pos) { //non-const版本
return const_cast<char&> //返回值转型,移除const
(static_cast<const className&>(*this)[pos]);//*this转型,增加const
}
反过来使用const版本调用non-const版本则是一种错误行为。
请记住:
将某些东西声明为const可以帮助编译器侦测除错误;const可以被施加在任何作用域内的对象、函数参数、函数返回类型、成员函数本身。
编译器强制实施bitwise constness,但是你编写程序时应该使用“概念上的常量性”(conceptual constness)。
当const和non-const成员函数有着实质等价的实现时,使用non-const版本来调用const版本可以避免代码重复。
永远在对象使用之前将其初始化,对于内置类型,请手动初始化;对于非内置类型,初始化的任务则落在了构造函数身上。
别混淆赋值和初始化的概念,例如:
class ABEntry {
public:
ABEntry(const string& name, const string& address, int num) {
//这里都是赋值,而非初始化,实际上是先调用default ctor,再赋值
_name = name;
_address = address;
_num = num;
}
private:
string _name;
string _address;
int _num;
}
更好的办法是使用所谓的成员初值列(member initialization list):
ABEntry::ABEntry(const string& name, const string& address, int num)
: _name(name), _address(address), _num(num) { }
//直接进行copy ctor,就是初始化
特别注意的是:在初值列中列出所有的成员变量,避免遗漏;另外,如果成员变量是const 或者reference,一定需要初值,而不能赋值,最简单的做法就是使用成员初值列。
C++有着固定的初始化顺序:按照声明顺序进行初始化,且基类先于派生类初始化。
class init {
public:
init(int i) : _j(i), _i(_j);
//这里会出现错误,因为先对_i初始化,再对_j初始化。
private:
int _i;
int _j;
}
对于local static object(局部静态对象),程序结束之后,该对象会被自动销毁。
基于局部静态对象的singleton设计模式:
class Singleton
{
public:
static Singleton& getInstance() {
static Singleton s;
return s;
}
private:
Singleton() {}
Singleton(const Singleton&);
void operator=(const Singleton&);
};
请记住:
为内置类型进行手工初始化,C++不保证初始化它们。
构造函数最好使用成员初值列,不要在构造函数中使用赋值操作,初值列中的成员变量的顺序应该和声明顺序一致。
为免除“跨编译单元之初始化次序”问题,使用返回local static对象引用的函数来代替non-local static对象。