让自己习惯C++(上)

01 将C++看做一个语言联邦

虽然C++强大且高效,但在使用时却经常有“例外情况”发生,以至于我们不明白C++到底在做什么或是该怎样做。最简单的方法就是不把C++作为一个单独的语言,而是很多种不同语言的结合体。在使用这些语言时,我们遵守他们各自的规则 。而这些分语言的规则往往是通俗易懂且容易记忆的。
我们可以把C++看做四种次语言的结合体。
1、C

  • C++是以C为基础的
  • 区块、语句、预处理器、内置数据类型、数组、指针都来自C
  • 当用C成分工作时,没有模板,没有异常,没用重载...

2、Object-Oriented C++

  • 面向对象的特性
  • classes(包括构造函数、析构函数)、封装、继承、多态、虚函数...

3、Template C++

  • 泛型编程部分
  • 在编程时考虑模板与相应的编程条款
  • TMP(模板元编程)

4、STL

  • 强大的标准库
  • 容器、迭代器、算法、函数对象的规约

这四种次语言拥有自己不同的守则和公约。分别熟悉他们后对于C++的理解和使用就会容易许多。


02 尽量以const、enum、inline替代#define

因为预处理器的机制,当我们使用#define时,很多情况下会导致莫名其妙的问题。因为#define并不被视为语言的一部分,而是在预处理过程中被替换掉。下面讨论三种情况。
1、以const替代#define
假设有这样一条语句

#define ASPECT_RATIO 1.653

虽然在大学上C语言课时老师经常会要求我们这样做,但这很可能导致调试时的错乱分析。
在这条语句里,ASPECT_RATIO可能从未被编译器看到,它在预处理阶段就被替换掉了。当我们使用这个常量但获得一个错误信息时,很可能会看到错误信息里包含1.653而不是它的名称。如果这个宏定义发生在并非由你写的头文件中,你可能会感到很困惑并且花费大量时间追踪它来自哪里。
为了解决这个问题,我们最好把使用的名称放入记号表。
下面是一个解决方案

const double AspectRatio = 1.653

很明显编译器会看到AspectRatio并且记入符号表内。同时,这样做可能会减少代码量:如果使用宏定义很可能目标码中会出现多份1.653,但改成常量后就不会出现。

当然,任何事情都有例外(尤其是当你使用C++时)
第一个:常量指针
因为常量定义一般都被放在头文件中,所以当使用指针时要将其声明为const。当我们定义一个常量字符串时,我们要写const两次:

const char* const authorName = "Scott Meyers";

而且,在真正使用C++时,string对象往往比它的前辈char*-based更适宜。所以这样定义一般更好。

const std::string authorName("Scott Meyers");

第二个:class专属常量
为了限制常量的作用域,需要让常量成为class的成员。而为了确保常量至多只有一份实体,我们要让它成为一个static成员。

class GamePlayer
{
private:
    static const int NumTurns = 5;    //常量声明式
    int scores[NumTurns];
    ...
};

类内的静态成员需要一个类外的定义式,所以要提供额外的定义式

const int GamePlayer::NUmTurns;

2、以enum替代#define
由于class常量已经在声明的时候获得了初值,所以在定义时不可以再设置初始值。
某些老旧的编译器可能并不支持声明时获得初值这种语法,所以我们要在定义式时设置初值。但是还有一些编译器坚持不允许static整型class常量完成上面的数组初始化。可以采用枚举类型来解决:

class GamePlayer
{
private:
    enum{ NumTurns = 5};
    int scores[NumTurns];
    ...
};

其实enum更像是#define而不是const:例如取一个const的地址是合法的,而取enum的地址不合法。如果不想让别人通过一个指针来获取某个整数常量,可以使用enum。
许多代码用到了enum hack,它是模板元编程的基础部分,所以还是有必要了解它的使用的。

3、以inline替代#define
看下面这个宏:

#define CALL_WITH_MAX(a,b) f((a) > (b) > (a) : (b))

这个宏定义看上去就给人一种很糟糕的感觉。当我们遇到这种情况时,使用一个template inline函数是更好的:

template<typename T>
inline void callWithMax(const T& a, const T& b)
{
    f(a > b ? a : b);
}

这是一个真正的函数,所以它遵守作用域和访问规则。

总结:

  • 对于单纯常量,最好用const对象或enums替换#define
  • 对于类似函数的宏,最好用inline函数替换#define
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。