1.视C++为一个语言联邦(View C++ as a federation of languages)
C++可分为4个主要的次级语言
1.C
2.Object - Oriented C++
3.Template C++
4.STL
2.尽量以const, enmu , inline 替换 #define(Prefer consts,enums,and inlines to #defines)
对于单纯常量,最好以const对象或enum替换#define
对于形似函数的宏,最好该有inline替换
使用#define的缺点:
1.记号名称并未进入记号表,被编译器当做常量替换处理,使得发生编译错误时不能追踪到#define所定义的名称。
2.会有各种宏问题,造成各种错误。
可以使用以下方法
使用const定义常量,可能比#define产生较小的程序大小。并解决问题1.
const double AspectRatio = 1.653;//常量第一个字母多为大写
const char* const Name_1 = "Queen";//将指针也声明为const
const std::string Name_2("Queen");//C++中使用string更好
class GamePlayer {
public:
static const int NumTurns = 5;//class专属常量
int scores[NumTurns];//使用class专属常量
};
//const int GamePlayer::NumTurns;//有些编译器需要在实现文件里提供定义
也可使用enum
class GamePlayer {
public:
enum {
NumTurns = 5
};
int scores[NumTurns];
};
使用inline代替宏
#define CALL_WITH_MAX(a,b) F( (a) > (b) ? (a) : (b) )//以较大者调用F
int a = 5, b = 0;
CALL_WITH_MAX(++a,b);
CALL_WITH_MAX(++a,b+10);
向上面这个宏,我们并不确定会不会调用++a两次,向此类的宏的缺点十分多,很不安全。使用inline代替。
template<typename T>
inline void callWithMax(const T& a, const T& b){
f(a > b ? a : b);//以较大者调用f
}
3.尽可能使用const (Use const whenever possible)
将某些东西声明为const可帮助编译器侦测出错误的用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型。成员函数本体。
编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”。
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
top-level const and low-level const
int i = 0;
int *const p1 = &i;//top-level const不能改变p1的值
int ci = 1;
const int *p2 = &ci;//low-level const不能通过p2改变ci的值
int cj = 2;
const int *const p3 = &cj;//不能改变p3的值也不能通过p3改变cj的值
迭代器中的const
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();//不能改变iter的值,即top-level const
const auto it1 = vec.begin();//同上
std::vector<int>::const_iterator cIter = vec.begin();//不能通过cIter改变vec.begin()的值,即low-level const
auto it2 = vec.cbegin();//同上
使用const可限制一些错误的发生
class Rational {
public:
Rational(int i):i(i){}
const Rational operator+ (const Rational& rhs){
return this->i + rhs.i;
}
int getI(){return i;}
private:
int i;
};
Rational a,b,c;
(a + b) = c;//ERROR:No viable overloaded'='
if (a + b = c)//ERROR:No viable overloaded'='
//可避免对计算结果赋值,=与==的错误
const成员函数
class TextBlock{
public:
TextBlock(std::string text):text(text){}
const char& operator[](std::size_t position) const{
return text[position];
}
char& operator[](std::size_t position){
return text[position];
}
private:
std::string text;
};
int main(int argc, const char * argv[]) {
TextBlock tb("Hello");
std::cout<<tb[0];//H
const TextBlock ctb("World");
std::cout<<ctb[0];//W
return 0;
}
在上例的const函数中
const char& operator[](std::size_t position) const{
return text[position];
}
我们不能通过改函数修改成员变量,因为函数后的const限定了修改成员变量。因为编译器想要bitwise constness,但有时我们是需要逻辑上的const即logical constness。我们可以在变量声明时加上mutable关键字,可在const函数中修改该变量。
const 版本的operator[]和non-const版本的operator[]中需要一些重复的操作,例如边界检验等。我们可以在non-const函数中调用const函数解决代码重复问题。
const char& operator[](std::size_t position) const{
//边界检验
//检验数据完整性
//.......
return text[position];
}
char& operator[](std::size_t position){
return
const_cast<char&>(//移除const operator[]的const
static_cast<const TextBlock&>(*this)//为this添加const
[position]
);
}
先用static_cast将*this转化为const类型以调用const版本的operator[],若不做次转化operator[]将无限调用自己。在用const_cast将const operator[]返回的内容去const。
不能在const版本的成员函数中调用non-const版本的成员函数,因为在non-const版本中可能会破坏其const性质。导致代码不安全。
4.确定对象被使用前已先被初始化
为内置型对象进行手工初始化,因为C++不保证初始化他们
构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
为免除跨编译单元之初始化次序问题,请以local static对象替换non-local static对象
C++对定义于不同的编译单元内的non-local static对象的初始化相对次序并无明确定义。
假如用一个non-local static对象初始化另一个对象,用来初始化的non-local static对象可能并未初始化。造成程序错误。可以使用local static对象替换non-local static对象。
extern ClassA obj;
//修改为
ClassA& obj(){
static ClassA obj;
return obj;
}
使用obj()代替obj