前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Effective C++ 第一章重点条款学习

Effective C++ 第一章重点条款学习

作者头像
公众号guangcity
发布2019-09-20 17:40:56
1K0
发布2019-09-20 17:40:56
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

导语

注:本节代码见github仓库

https://github.com/Light-City/effective_cpuluplus

条款2:尽量使用const, enum, inline, 减少宏变量#define的使用

尽量多用编译器,少用预处理器

代码语言:javascript
复制
#define A 3.14

替换为:

代码语言:javascript
复制
const double A = 3.14;

注意宏是全局的与范围无关!

当我们以const替换#define,两种特殊情况值得说明:

(1) 定义常量指针

例如若要在头文件内定义一个常量的(不变的)char *字符串,就必须写const两次

代码语言:javascript
复制
const char* const myWord = "xxx";

而对于上述在C++中直接使用一次const即可:

代码语言:javascript
复制
const std::string myWord("xxx");

(2)class专属常量

例如:为了将常量的作用域(scope) 限制于 class内,你必须让它成为class 的一个成员 (member) ;而为确保此常量至多只有一份实体,你必须让它成为一个static 成员:

代码语言:javascript
复制
class GamePlayer {
private:
    static const int NumTurns=5;
    int scores[NumTurns];
...
};

注意,因为此处是类的成员声明范围内,所以上面只是变量的声明和初始化,而并非定义,因此如果想获取变量的地址,需要在别处另加定义。这个定义不能有任何赋值语句,因为在类内已经规定为const:

代码语言:javascript
复制
const int GamePlayer::NumTurns;

使用枚举

当你在一个类内声明某变量,但你的编译器不允许在声明时赋值初始化:

代码语言:javascript
复制
int a;
int s[a];

此时s[a]肯定报错了,为了解决这种问题,可以使用枚举:

代码语言:javascript
复制
enum {a=5};
int s[a];

inline函数替代宏函数

inline关键字用来建议编译器把某频繁调用的函数当做内联函数,即在每次函数调用时,直接把函数代码放在函数调用语句的地址,减少堆栈浪费。

现在有如下例子:

代码语言:javascript
复制
#define CALL_MAX(a,b) f((a) > (b) ? (a) : (b))

当main函数中调用如下:

代码语言:javascript
复制
cout<<MAX(++a, b)<<endl;              // a被增加两次
cout<<MAX(++a, b+10)<<endl;           // a被累加一次

这与我们的预期结果不同!

为了解决这个问题,我们采用inline函数:

代码语言:javascript
复制
template<typename T>
inline int Max(const T& a, const T& b){
    return (a>b ? a:b);
}

这样就避免了前面宏替换被累加两次的问题.

总结:对于常量,原先写的宏用const或者enum来替换,宏函数用inline修饰的函数!

本节额外补充:const 有地址,enum与#define没有地址原因:

  • const 定义的实际是一个变量,const只限定它不能被修改,所有变量都可在程序运行时获取其地址
  • enum类型中的枚举项只是enum类型声明的一部分,它不是定义出来的变量,所以不能取地址
  • #define出来的是宏,它是预处理的东西,预处理后的编译阶段已经不存在,所以也不可能获取宏的地址

条款3:尽可能使用const关键字

这一节内容详细见下面:

https://github.com/Light-City/CPlusPlusThings/tree/master/const

条款4:确定对象被使用前已被初始化

(1)内置类型初始化

定义的时候我们一般这样写:

代码语言:javascript
复制
int a=0;

而不应该直接写

代码语言:javascript
复制
int a;

(2)类的初始化

用户自定义的类,我们需要构造函数初始化列表来完成此类的初始化.

代码语言:javascript
复制
A::A(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones){
    theName = name;
    theAddress = address;
    thePhones = phones;
}

函数里面是赋值而非初始化!正确方式如下:

代码语言:javascript
复制
A::A(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones)
    :theName(name),
    theAddress(address),
    thePhones(phones)
{}

(3)引用必须被初始化

代码语言:javascript
复制
int a = 1;
int& b = a;        

(4)初始化no-local static对象

现有一个场景:在两个编译单元中,分别包含至少一个no-local static对象,当这些对象发生互动时,它们的初始化顺序是不确定的,所以直接使用这些变量,就会给程序的运行带来风险。

编译单元(translation unit): 可以让编译器生成代码的基本单元,一般一个源代码文件就是一个编译单元。

非本地静态对象(non-local static object): 静态对象可以是在全局范围定义的变量,在名空间范围定义的变量,函数范围内定义为static的变量,类的范围内定义为static的变量,而除了函数中的静态对象是本地的,其他都是非本地的。

回到问题,现有以下服务器代码:

代码语言:javascript
复制
class Server{...};    
extern Server server;                 //在全局范围声明外部对象server,供外部使用

又有某客户端:

代码语言:javascript
复制
class Client{...};
Client::Client(...){
    number = server.number;
}

Client client;                       //在全局范围定义client对象,自动调用了Client类的构造函数

以上问题在于,定义对象client自动调用了Client类的构造函数,此时需要读取对象server的数据,但全局变量的不可控性让我们不能保证对象server在此时被读取时是初始化的。试想如果还有对象client1, client2等等不同的用户读写,我们不能保证当前server的数据是我们想要的。

幸运的是一个小小的设计便可完全消除这个问题。唯一需要做的是:将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static) 。这些函数返回一个 reference 指向它所含的对象。然后用户调用这些函数,而不直接指涉这些对象。换句话说, non-local static 对象被 local static 对象替换了。

解决方法: 将全局变量变为本地静态变量

使用一个函数,只用来定义一个本地静态变量并返回它的引用。因为C++规定在本地范围(函数范围)内定义某静态对象时,当此函数被调用,该静态变量一定会被初始化。

代码语言:javascript
复制
class Server{...};

Server& server(){                         //将直接的声明改为一个函数
    static Server server;
    return server;
}

class Client{...};

Client::client(){                        //客户端构造函数通过函数访问服务器数据
    number = server().number;
}

Client& client(){                        //同样将客户端的声明改为一个函数
    static Client client;
    return client;
}

参考资料:

Effective C++ 第3版

https://zhuanlan.zhihu.com/p/64141116

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 条款2:尽量使用const, enum, inline, 减少宏变量#define的使用
  • 条款3:尽可能使用const关键字
  • 条款4:确定对象被使用前已被初始化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档