【Effective C++】让自己习惯C++

一、视C++为一个语言联邦

  C是面向过程的语言,它的侧重点在于算法和数据结构。编写C代码,侧重点在于通过输入数据,设计一定的算法过程,得到输出。C++是面向对象的语言,它的侧重点在于抽象出对象模型,使这个模型和问题像契合。通过对对象状态的控制,来解决问题。

1、C++不同于C的部分

  class:虽然C语言也有结构体struct,但是它更多的侧重于数据机构,侧重数据的组织。虽然struct在++中也支持class的个各种特性,但是很少用struct去替代class。它们两个一个不同在于class默认成员的访问是private,而struct是public。

  template:模板属于泛型编程,泛型编程使得代码独立于特定的数据类型,可以大大减少代码量。

  overload:重载是C语言中没有的,在一些代码中经常看到external “C",这是表示以C语言方式编译。因为重载是通过编译时,在函数明后加上函数参数类型来生成函数名实现的,而C语言则不是,所以如果要给C调用,就要加上extern “C”。

2、可以将C++分为4个层次

  • C:C++实在C语言的基础上发展而来的。
  • Object-Oriented C++:这是C++中不同于C的部分,这里主要指面向对象。
  • Template C++:C++中的泛型编程。
  • STL:这是一个标准模板库,它用模板实现了很多容器、迭代器和算法,使用STL往往事半功倍。

二、尽量以const,enum,inline替换#define

1、#define存在的问题

【不容易定位错误】:

#define PI 3.1415926

  在预处理时, 所有使用PI的地方都将被替换,之后编译器在编译时从未看到过PI。这时如果遇到错误,报错时给出的是3.1415926,而不是PI,因为PI从未进入到符号表,这将导致错误难以理解。一个替换的方法是如下定义:

const double PI = 3.1415926;

【class专属常量】:

  专属于class作用域的常量。专属于class的常量将这个常量限定在class的作用域内,而#define定义的常量没有作用域的限制,一旦在某一处有个宏定义,在其后面都有效(除非#undef)。这意味着#define不仅不能够用来定义class专属常量,也不能提供任何封装性。

class GamePlayer
{
	static const int NumTurns=5; // 所有实例共享一份
	int scores[NumTurns];
};

2、const,enum,inline的好处

  • const的好处:
    • define直接常量替换,出现编译错误不易定位(不知道常量是哪个变量);
    • define没有作用域,const有作用域提供了封装性。
  • enum的好处:
    • 提供了封装性;
    • 编译器肯定不会分配额外内存空间(其实const也不会)。
  • inline的好处:
    • define宏函数容易造成误用。

3、请记住

  • 对于单纯常量,最好以const对象或者enum替换#define

  • 对于形似函数的宏,最好改用inline函数替换#define


三、尽可能使用const

1、const修饰变量

// 表示a是一个常量。
const T a; 
//表示a是一个指向常量的指针,*a是不能被修改的,但是a可以被修改。
const T *a; 
// 表示a是一个指向整数的常指针,a是不能被修改的,但是*a是可以被修改的。
T * const a; 
// 表示a是一个指向常量的常指针,*a是不能被修改的,a也是不能被修改的。
const T * const a; 

2、const修饰函数参数

  传递过来的参数在函数内不能改变,与修饰变量性质一样。

void display(const int a, int b)
{
	a=5; // 错误。
}

3、const成员函数

  const成员函数的目的是确认该函数可以用到const对象上。const成员函数使得:

  • class接口更加容易理解,确认哪些接口可以修改class成员。

  • 使操作const对象成为可能。

  关于第2点,是因为const对象只能调用const成员函数,但是非const对象既可以调用普通成员函数,也可以调用const成员函数。这是因为this指针可以转换为const this,但是const this不能转换为非const this。

【Note】:
(1)一个函数是不是const是可以被重载的。
(2)如果某些成员变量可能总是会被修改,即使是在const成员函数中,可以对这一类变量使用mutable关键字。

4、函数返回const

  若函数的返回值是指针,且用const修饰,则函数返回值指向的内容是常数,不可被修改,此返回值仅能赋值给const修饰的相同类型的指针。令函数返回一个常量值,往往可以降低客户错误而造成的意外,又不至于放弃安全性和高效性。

Rational a, b, c;
(a * b) = c; // 避免无意义的赋值操作。

5、请记住

  • 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。

  • 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)。

  • 当const 和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复


四、确定对象使用前已被初始化

1、成员初始化列表

  在一些语境下会初始化为0,但在另一些语境下可能就不会初始化,例如:

class Point
{
	int x, y;
}

  在构造函数中完成初始化时,要区分赋值和初始化。在构造函数体内的是赋值,在初始化列表中的才是初始化。

class Point
{
public:
	Point(int x_, int y_):x(x_), y(y_) { }
private:
	int x, y;
}

  初始化效率往往高于赋值。赋值是先定义变量,在定义的时候可能已经调用了变量的构造函数,之后赋值是调用了赋值操作符;而初始化是直接调用了复制构造函数

【Note】:
(1)初始化顺序要和声明顺序一致。
(2)在一些情况下必须使用初始化方式,有些遍历在定义时就要求初始化,例如const和引用

2、以local static对象替换non-local static对象

  将每个non-local static对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。然后用户调用这些函数,而不是直接指涉这些对象。换句话说,non-local static对象被local static对象替换了。

class FileSystem{
  /..../
  std::size_t numDisks() const;   
  /..../
};

FileSystem& tfs()
{
    static FileSystem fs;
    return fs;
}

class Directory {   
   /..../
   Directory (params);
   /..../
};

Directory::Directory(params)
{
     /..../
     // 如果是tfs.numDisks()则无法保证tfs在tempDir之前被初始化。
     std::size_t disks = tfs().numDisks();
     /..../
}

Directory& tempDir()
{
    static Directory td;
    return td;
}

3、请记住

  • 为内置型对象进行手工初始化,因为C++不保证初始化它们(C part of C++)。

  • 构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。

  • 为避免“跨编译单元之初始化次序”问题,请以local static 对象替换 non-local static对象

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~青萍之末~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值