目录
条款01:视C++为一个语言联邦(View C++ as a federation of languages)
C++有4个主要的次语言(sublanguage):
- C。包含区块(blocks)、语句(statements)、预处理器(preprocessor)、内置数据范例(built-in data)、数组(arrays)、指针(pointers)等;没有模板(templates)、异常(exceptions)、继承(inheritance)。
- Object-Oriented C++。这是C with classes部分,包含classes(包罗构造函数和析构函数)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数等。
- Template C++。这是C++泛型编程(generic programming)部分。
- STL。涉及容器(containers)、迭代器(iterators)、算法(algorithms)、函数对象(function objects)。
Tips:
- C++高效编程守则视情况而变革,和使用的次语言种类有关
条款02:只管以const、enum、inline替换#define(Prefer consts, enums, and inlines to #define)
该条款可表达为:宁可以编译器替换预处理器
替换原因
- #define ASPECT_RATIO 1.653
- const double AspectRatio = 1.653; //以常量替换宏
复制代码 替换原因:
- 暗号名称ASPECT_RATIO可能在编译器开始处理源码之前被预处理器移走,而未被编译器看到,没有进入暗号表(symbol table),故在编译错误涉及该常量时难以确定1.653的泉源。使用语言常量AspectRation则不会有这个问题。
- 对浮点常量(floating point constant,如本例),使用常量可能比使用#define导致更少量的码,因为预处理器盲目标将宏名称替换为1.653可能导致目标码(object code)出现多份1.653,改用常量AspectRatio则不会有此问题
两种特殊常量
常量指针(constant pointers):常量定义式常位于头文件,故有必要将指针声明为const- const char* const authorName = "Scott Meyers";
- const std::string authorName("Scott Meyers"); //使用string更合适
复制代码 class专属常量:为了将常量作用于(scope)限制在class内,需要让其成为class的一个成员(member);为了确保此常量至多只有一份实体,需要让其成为static成员- class GamePlayer {
- private:
- static const int NumTurns = 5; //常量声明式
- int scores[Numturns]; //使用该常量
- ...
- };
复制代码 当某个东西是class专属常量+static+整数范例(integral type,如ints、chars、bools),只要不取地址,则可是有声明式而无定义式,否则需要提供定义式。- //应放入实现文件而非头文件
- const int Gameplayer::NumTurns; //NumTurns的定义,声明时设定了初值故此处可不设定值
复制代码 旧编译器可能不允许static成员在声明式上获得初值,此时可将初值放在定义式。- class CostEstimate {
- private:
- static const double FudgeFactor; //static class常量声明,位于头文件
- };
- const double CostEstimate::FudgeFactor = 1.35; //static class常量定义,位于实现文件
复制代码 若译器不允许static成员在声明式上获得初值,且class编译期间需要一个class常量值(如存在数组声明式),则可用“the enum hack”补偿,使用枚举范例(enumerated type)的数值可充当ints使用的特点。
enum hack有以下特点:
- 取enum地址不合法,可避免存在指向其的pointer或reference,进而不会导致非必要的内存分配
- “enum hack”是template programming(模板元编程)的基础技术
- class GamePlayer {
- private:
- enum { NumTurns = 5 }; //令NumTurns成为5的一个记号名称
- int scores[NumTurns];
- ...
- };
复制代码 形似函数的宏
类似函数的宏(macros)没有函数调用(function call)带来的额外开销,但其缺点显著,最好替换为inline函数- //带宏实参的宏,每个实参都需要加上小括号,然而还是可能出现难以预料的问题
- #define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
- //使用template inline实现宏的高效以及函数的可预料性和类型安全性(type safety)
- template<typename T>
- inline void callWithMax(const T& a, const T& b)
- {
- f(a > b ? a : b);
- }
复制代码 Tips:
- 对于单纯常量,最好用const或enums替换#define
- 对于形似函数的宏,最好用inline函数替换#defines
条款03:尽可能使用const(Use const whenever possible)
const和指针
- 常量指针:const在星号*左边,则被指物是常量
- 指针常量:const在星号*右边,则指针自身是常量
- void f1(const Widget* pw); //被指物是常量
- void f2(Widget const * pw); //同上
复制代码 STL迭代器的作用类似T*指针,其同样有指针常量和常量指针的用法- std::vector<int> vec;
- ...
- const std::vector<int>::iterator iter = vec.begin(); //指针常量
- *iter = 10; //正确,改变iter所指物
- ++iter; //错误!iter本身是const
- std::vector<int>::const_iterator cIter = vec.begin(); //常量指针
- *cIter = 10; //错误!*cIter是const
- ++cIter; //正确,改变cIter本身
复制代码 const成员函数
const成员函数有两个作用:
- 使class接口容易被理解
- 使操作const对象成为可能
[code]//两个成员函数如果只是常量性(constness)不同,可以被重载class TextBlock {public: ... const char& operator[](std::size_t position) const //对于const对象的操作符[] { return text[position]; } char& operator[](std::size_t position) //对于non-const对象的操作符[] { return text[position]; }private: std::string text;};//operator[]使用方式如下TextBlock tb("Hello");std::cout |