effective c++
条款01:视C++为一个语言联邦
1、C++高效编程守则视状况而变化,取决于使用C++哪一部分:
sublanguage(次语言):
C: blocks、statements、preproessor、built-in data types、arrays、pointers etc
Object-Oriented C++: class、encapsulation、inheritance、polymorphism、virtual etc
Template C++: programming paradigm (aka template metaprogramming, namely TMP)
STL: containers、iterators、algorithms、function objects
条款02、尽量以const、enum、inline替换#define
1、 尽量用编译器代替预处理器,编译器会做类型检查
const double AspectRatio = 1.6;
const char* const authorName = "Tom"; // 常量指针常量
2、注意:
1)尽量使用string替代char* : const string authorName(“Tom”);
2)为了将常量的作用域(scope)限制于class内,必须让他成为class的一个成员(member);而为确保此常量至多只有一份实体,必须让它成为一个static成员:
class GamePlayer{
private:
static: const int NumTurns = 5; // 常量声明式
int scores[NumTurns]; // 使用该常量
}
3、如果不想让别人获得某个整形的pointer或refrence,可以使用enum(类似#define)
class GamePlayer{
private:
enum{ NumTurns = 5 };
int scores[NumTurns];
}
4、使用inline代替#define
#define max(a, b) f((a) > (b) ? (a) : (b))
即使给所有实参加括号,但仍然会发生不可预料的错误,如
int a = 5, b = 10;
max(++ a, b); // a 累加两次
max(++ a, b + 10); // a 累加一次
使用template inline代替上述宏定义:
template<typename T>
inline void max(const T& a, const T& b){
f(a > b? a : b);
}
结论
- 对于单纯常量,最好以const或enums代替#define
- 对于形似函数的宏(macros),最好改用inline函数替换#define
条款03:尽可能使用const
1、const如果出现在*左边,表示被指物是常量;如果在右边,表示指针自身是常量;如果在两边,表示被指物和指针都是常量。
const int* p = 1; // aka int const* p = 1; non-const pointer, const data;
int* const p = 1; // const pointer, non-const data;
const int* const p = 1; // const data, const pointer;
2、使用const修饰迭代器就像声明指针为const一样(aka T* const);若想要迭代器指向的内容不可改动,应使用const_iterator;
std::vector<int> vec;
const auto iter = vec.begin(); // aka T* const iter;
iter ++; // error !
auto citer = vec.cbegin(); // aka const T*;
// std::vector<int>::const_iterator
*citer = 10; // error!
3、const可以修饰函数返回值,预防if(a * b = c)
这种动作。
const Rational operator*(const Rational& lhs, const Rational& rhs);
4、成员函数是const
bitwise constness(physical constness) : 成员函数不可以更改对象内的任何一个bit,因此const成员函数不可以更改对象内任何non-static成员变量;但是在包含指针时结果可能不尽人意
class CTextBlock{
public:
//inappropriate (but bitwise const) declaration of operator[]
char& operator[](std::size_t position) __const__{ // 禁止更改非静态成员变量
return p[position];
}
private:
char* p;
}
const CTextBlock cctb("Hello"); // declare constant object
char* pc = &cctb[0]; // call the const operator[] to get a pointer
*pc = 'j'; // cctb = "jello" , still can change its content
__logical constness:__在观测者的角度不会改变成员的值
class CTextBlock{
public:
...
std::size_t length() const;
private:
char* pText;
std::size_t textLength; // last calculated length of textblock
bool lengthIsValid; // whetherlength is currently valid
}
std::size_t CTextBlock::length() const
{
if(!lengthIsValid){
textLength = std::strlen(pText); // cant assign to textLength
lengthIsValid = true; // lengthIsValid in a const member function
}
return textLength;
}
可以使用mutable 关键字来修饰其中需要改变的变量。如
mutable std::size_t textLength;
mutable bool lengthIsValid;
这两个变量时私有变量,不会暴漏给使用者,故这个程序在逻辑上是constness
5、在const和非const成员函数中避免代码复制,一些类中,const成员函数和non-const成员函数功能类似,在这两个函数中都要执行相同的代码,如:
class CTextBlock{
public:
const char& operator[](std::size_t position)const
{
prepare();//准备
return pText[position];
}
char& operator[](std::size_t position)
{
prepare();//准备
return pText[position];
}
void prepare()const;//一些准备动作
char * pText;
int length;
};
如果两个方法处理逻辑差不多,可以是用类型转换,把non-const转换为const,再转换其返回值类型,是一个避免代码重复的安全方法
class CTextBlock{
public:
const char& operator[](std::size_t position)const
{
prepare();//准备
return pText[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return const_cast<char&>( \ // cast away const on op[]'s return type
static_cast<const CTextBlock&>(*this) \ // add const to *this'type
[position]); // call const version of op[]
}
void prepare()const;//一些准备动作
char * pText;
int length;
};
但是,const的函数不能强制转化为非const的函数。