当前位置: 首页 > news >正文

《Effective C++》第三版——让自己习惯C++

参考资料:

  • 《Effective C++》第三版

注意:《Effective C++》不涉及任何 C++11 的内容,因此其中的部分准则可能在 C++11 出现后有更好的实现方式。

条款 1:视 C++ 为一个语言联邦

可以将 C++ 视作一个由多个次语言构成的语言联邦,不同次语言可能有不同的高效编程守则

C++ 中主要的次语言有 4 个:C、Object-Oriented C++、Template C++、STL。

条款 2:尽量以 constenuminline 替换 #define

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

通过 #define 定义常量,例如:

#define PI 3.14

PI 将在预处理阶段被“移走”,导致 PI 没有进入记号表、不能被编译器看见,这可能给后续调试带来麻烦。

使用 const 替换 #define,可以确保名字能被编译器看到:

const double pi = 3.14;

此外,使用 const 还能实现可以将常量的可见范围限定在某个作用域(如 class 内部),而 #define 不具备此功能。

在某些特殊情况里,我们也可以用 enum 实现 int 常量:

class A{
private:// 这里的特殊情况指,某些编译器(错误地)不允许为static整型class常量提供类内初始值enum { Num = 5 };int arr[Num];
}

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

使用 #define 定义宏可能会导致很多问题:

// 对较大者调用f
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))	// 已经足够小心地为每个实参添加小括号int a = 5, b = 0;
CALL_WITH_MAX(++a, b);	// a累加2次
CALL_WITH_MAX(++a, b+10);	//a累加1次

利用 template inline 可以兼顾效率和安全性:

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

条款 3:尽可能使用 const

将某些东西声明为 const 可帮助编译器侦测出错误用法

  • 对于指针,const 出现在 * 前代表被指物是常量,出现在 * 后代表指针本身是常量;
  • 对于迭代器,用 const 修饰一个迭代起类型,代表这个迭代器是常量,const_iterator 代表被指物是常量;
  • 对于函数参数,如果不要在函数内部对其进行修改,就将它们声明为 const

编译器强制实施 bitwise constness,但编写程序时还要注意 conceptual constness

设计 const 成员函数,不仅可以明确地表示哪些成员函数可以改动对象内容,哪些不行,还可以使操作 const 对象成为可能。

需要注意的是,对于 const 成员函数,编译器只保证 bitwise constness,即不更改对象的任何成员。然而,如果对象中包含指针,编译器却允许 const 成员修改指针所指的内容。另外,如果我们希望绕过 bitwise constness,可以将成员声明为 mutable

constnon-const 成员函数功能相似时,令 non-const 版本调用 const 版本可以减少代码重复

constnon-const 成员函数功能相似时,不应令 const 版本调用 non-const 版本,因为这会带来“对象被修改”的风险;而应令 non-const 版本调用 const 版本。

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

对内置对象进行手工初始化,因为 C++ 不保证初始化它们

C++ 的初始化规则较为复杂,只需要记住所有对象使用之前必须初始化,对于内置类型,必须手工完成此事

构造函数最好是用成员初值列,顺序应与其在 class 中的声明顺序相同

为避免跨编译单元初始化次序问题,应以 local static 对象代替 non-local static 对象

考虑下面的情形:

class A {
public:void f();
};extern A a;		// 预留给客户使用的对象
A a;
// 客户的文件
extern A a;class B {void g() {a.f();}
};B b;
b.g();	// 不能确保g()被调用前,a已经初始化

C++ 对于定义在不同编译单元non-local static 对象的初始化顺序并无明确定义,所以无法确保 b.g() 执行前 a 已经被初始化。

解决方法是,将 non-local static 对象的初始化放入函数中:

A& init(){	// 第一行定义static对象,第二行返回,可以考虑定义为inlinestatic A a;return a;
}
class B {void g() {init().f();}
};

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [性能]高速收发的TCP/MQTT通信
  • 【最新综述】基于深度学习的超声自动无损检测(下)
  • VirtualBox 克隆已有的虚拟机
  • 小项目建议用redis替换mq
  • Mysql系列-索引简介
  • qt-creator-10.0.2之后版本的jom.exe编译速度慢下来了
  • AI逻辑推理入门
  • FFT提取图像特征
  • harmony初学者入门
  • 【iOS】dismiss多级的方法
  • Xilinx系FPGA学习笔记(九)DDR3学习
  • Gitee注册-添加公钥-建立本地仓库
  • 原生 input 中的 “type=file“ 上传文件
  • LeetCode 每日一题 2024/9/9-2024/9/15
  • Linux常见查看文件命令
  • python3.6+scrapy+mysql 爬虫实战
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Android优雅地处理按钮重复点击
  • avalon2.2的VM生成过程
  • ES10 特性的完整指南
  • gcc介绍及安装
  • Java 网络编程(2):UDP 的使用
  • Javascript基础之Array数组API
  • k8s如何管理Pod
  • Meteor的表单提交:Form
  • web标准化(下)
  • 从伪并行的 Python 多线程说起
  • 力扣(LeetCode)56
  • ​HTTP与HTTPS:网络通信的安全卫士
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (二开)Flink 修改源码拓展 SQL 语法
  • (分布式缓存)Redis哨兵
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (四)模仿学习-完成后台管理页面查询
  • (学习日记)2024.01.19
  • (循环依赖问题)学习spring的第九天
  • (一)模式识别——基于SVM的道路分割实验(附资源)
  • (原)本想说脏话,奈何已放下
  • (转)LINQ之路
  • (转)项目管理杂谈-我所期望的新人
  • *1 计算机基础和操作系统基础及几大协议
  • .NET Core中的去虚
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET Remoting学习笔记(三)信道
  • .net wcf memory gates checking failed
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET大文件上传知识整理
  • ::
  • @WebServiceClient注解,wsdlLocation 可配置
  • []我的函数库
  • [28期] lamp兄弟连28期学员手册,请大家务必看一下
  • [Algorithm][动态规划][01背包问题][目标和][最后一块石头的重量Ⅱ]详细讲解
  • [CareerCup] 12.3 Test Move Method in a Chess Game 测试象棋游戏中的移动方法