01:视C++为一个语言联邦
C++是个多重范型编程语言multiparadigm programming language,一个同时支持过程形式procedural, 面向对象形式 object-oriented,函数形式 functional,泛型形式generic,元编程形式metaprogramming的语言。
简单方法就是将C++视为一个由相关语言组成的联邦而非单一语言。在其某个次语言中sublanguage,各种守则与通列都倾向简单、直观易懂、并且容易记住。然而当从一个次语言移往另一个次语言,守则可能改变。有4个如下主要的次语言:
(1) C。C++是以C为基础的。许多时候C++对问题的解法不过是较高级的C解法。
(2)Object-Oriented C++。这部分是C with Classes所诉求的,classes,封装,继承,多态,虚函数(动态绑定)……等。这一部分是面向对象设计守则在C++上的最直接实施。
(3)Template C++。这是C++的泛型编程generic programming部分,大多数程序员经验最少的部分。由于template威力强大,带来了崭新的编程范型,也就是所谓的template metaprogramming 模板元编程。
(4) STL。 STL是一个template程序库。它对日期、迭代器、算法以及函数对象的规约有极佳的紧密配合与协调,然而template及程序库也可以其他想法建置出来。
C++并不是一个带有一组守则的一体语言:它是从四个次语言组成的联邦政府,每个次语言都有自己的规约。
C++高效编程守则视情况变化而变化,取决于你使用C++的哪一部分。
02:尽量以const, enum, inline,替换#define (宁可以编译器替换预处理器,因为#define 不被视为语言的一部分)
Eg:
#define ASPECT_RATIO 1.653
记号名称ASPECT_RATIO也许从未被编译器看见;也许在编译器开始处理源码之前它就被预处理器移走了。于是记号ASPECT_RATIO名称有可能没有进入记号表内symbol table.
解决方法:是用一个常量替换上述的宏(#define)。
const double AspectRatio =1.653;//大写名称通常用于宏,此处改变名称写法
作为一个语言常量。
AspectRatio肯定能被编译器看到,当然就会进入记号表内。此外对浮点常量而言,
使用常量可能比使用#define导致较小量的码,因为预处理器“盲目将宏名称ASPECT_RATIO换成1.653”可能导致目标码出现多份1.653,若改用常量AspectRatio绝不会出现相同情况。
两种特殊情况:
(1)定义常量指针constant pointer,由于常量定义式通常被放在头文件内(以便被不同的源码),因此有必要将指针声明为const。若要在头文件内定义一个常量的char *-based字符串,必须写const两次。
const char * const authorName ="Scott Meyers";
const std::string authorName("Scott Meyers");
(2)class专属常量。为了将常量的作用域scope限制于class内,你必须让它成为class的一个成员member;而为确保此常量至多只有一份实体,你必须让它成为一个static成员。
class GamePlayer {
private:
static const int NumTurns=5;//常量声明式
int scores[NumTurns];//使用该常量
};
通常C++要求对所使用的任何东西提供一个定义式, 但如果它是个class专属常量又是static且为整数类型(integral type,例如ints,chars,bools),则需特殊处理。只要不取它们的地址,你可以声明并使用它们而无须提供定义式。但如果取某class专属常量的地址,或纵使你不取其地址而你的编译器却坚持要看到一个定义式,你就必须另外提供定义式如下:
const int GamePlayer::NumTurns;//Numturns的定义
注意,无法利用#define创建一个class专属常量,因为#defines并不重视作用域scope。一旦宏被定义,它就在其后的编译过程中有效(除非在某处被#undef)。这意味着#defines不仅不能够用来定义class专属变量,也不能够提供任何封装性,也就是说没有所谓private #define这样的东西。而const成员变量是可以被封装的。
所谓的“in-class初值设定”也只允许对整数常量进行。
class CostEstimate{
private:
static const double FudgeFactor;//static class常量声明位于头文件内
};
const double
CostEstimate::FudgeFactor = 1.35;//static class常量定义 位于实现文件内
编译器坚持必须在编译期间知道数组的大小。若编译器(错误地)不允许“static整数型class常量”完成“in class初值设定”,可改用所谓的“the enum hack”补偿做法。其理论基础是:”一个属于枚举类型enumerated type的数值可权充ints被使用”。
class GamePlayer {
private:
enum {
NumTurns = 5 //the enum hack 令NumTurns成为5的一个记号名称
};
int scores[NumTurns];
};
第一,enum hack的行为某方面说比较像#define而不像const,有时这正是你想要的。例如取一个const的地址是合法,但取一个enum的地址是不合法,取#define地址也不合法。
第二,实用主义。
只要写出template inline函数,可以获得宏带来的效率以及一般函数的所有可预料行为和类型安全性type safety。
//由于不知道T是什么,所以才用pass by reference-to-const
template<typename T>
inline void callWithMax(const T& a,const T& b) {
f(a>b ? a:b);
}
有了consts、enums和inlines,我们对预处理器(特别是#define)的需求降低了,但并非完全消除。#include仍然是必需品,而#ifdef/#ifndef也继续扮演控制编译的重要角色。
注意:对于单纯常量,最好以const对象或enums替换#defines。 对于形似函数的宏,最好改用inline函数替换#defines。
P11~P17