1、参考引用
- C++高级编程(第4版,C++17标准)马克·葛瑞格尔
2、建议先看《21天学通C++》 这本书入门,笔记链接如下
- 21天学通C++读书笔记(文章链接汇总)
1. 为代码编写文档
- 在编程环境下,文档通常指源文件中的注释
1.1 使用注释的原因
1.1.1 说明用途的注释
- 使用注释的原因之一是:说明客户如何与代码交互。通常而言,开发人员应当能够根据函数名、返回值的类型以及参数的类型和名称来推断函数的功能。但是,代码本身不能解释一切。有时,一个函数需要一些先置条件或后置条件,而这些需要在注释中解释。函数可能抛出的异常也应当在注释中解释
- 示例:C++ 中无法说明数据库对象的 saveRecord() 方法只能在 openDatabase() 方法之后调用,否则将抛出异常
/* Throws: DatabaseNotOpenedException if the openDatabase() method has not been called yet. */ RecordID saveRecord(Records record); // RecordID 只是 int 的别名,但用途一目了然
注意:编写注释时,那些可通过函数名返回值类型以及形参的类型和名称明显看出的信息,就不必添加到注释中
1.1.2 用来说明复杂代码的注释
- 对于专业领域中较为复杂的代码,较好的做法是使用注释描述所使用的算法,并说明(循环的)不变量。不变量是执行一段代码的过程中必须为真的条件,例如循环的迭代条件
1.1.3 传递元信息的注释
- 使用注释的另一个原因是:在高于代码的层次提供信息。元信息提供创建代码的详细信息,但不涉及代码的特定行为。例如,某组织可能想使用元信息跟踪每个方法的原始作者。还可使用元信息引用外部文档或其他代码
- 下例给出了元信息的几个实例,包括文件的作者、创建日期、提供的特性。此外还包括表示元数据的行内注释,例如对应某行代码的 bug 编号,提醒以后重新访问时代码中某个可能的问题
1.2 注释的风格
1.2.1 每行都加入注释
- 避免缺少文档的方法之一是在每行都包含一条注释。每行都加入注释,可以保证已编写的所有内容都有特定的理由。但在实际中,如果代码非常多,过多的注释会非常混乱、繁杂,无法做到
- 通常没必要给每行代码都添加注释,但当代码非常复杂,需要这样做时,不要只是将代码翻译成英语,而要解释代码实际上在做什么
if (result % 2 == 0){} // 错误:如果 result 对 2 求模结果为 0 if (result % 2 == 0){} // 正确:如果 result 为偶数
1.2.2 前置注释
- 团队可能决定所有的源文件都以标准注释开头,可以在该位置记录程序和特定文件的重要信息。在每个文件顶部加入的说明信息有
- 最近的修改日期
- 原始作者
- 前面所讲的修改日志文件
- 给出的功能 ID
- 版权信息
- 文件或类的简要说明
- 未完成的功能
- 已知的 bug
1.2.3 固定格式的注释
- 以标准格式编写可被外部文档生成器解析的注释是一种日益流行的编程方法。对于 C++ 而言,免费工具 Doxygen(www.doxygen.org) 可解析注释,自动生成HTML文档、类图、UNIX man 页面和其他有用文档
1.2.4 特殊注释
- 在添加注释前,首先考虑能否通过修订代码来避免使用注释。例如,重命名变量、函数和类,重新排列代码步骤的顺序,引入完好命名的中间变量等
- 如果处理不太明显的 API,应在解释 API 的地方包含对 API 文档的引用
- 更新代码时记得更新注释。如果代码的文档中充斥着错误信息,会让人非常困惑
- 如果使用注释将某个函数分为多节,考虑这个函数能否分解为多个更小的函数
2. 分解
- 分解是指将代码分为小段,理想状态下,每个函数或方法都应该只完成一个任务
- 任何非常复杂的子任务都应该分解为独立的函数或方法
2.1 通过重构分解
- 重构(refactoring)指重新构建代码的结构,下面给出了一些可用来重构代码的技术
- 增强抽象的技术
- 封装字段:将字段设置为私有,使用获取器和设置器方法访问它们
- 让类型通用:创建更通用的类型,以更好地共享代码
- 分割代码以使其更合理的技术
- 提取方法:将大方法的一部分转换成便于理解的新方法
- 提取类:将现有类的部分代码转移到新类中
- 增强代码名称和位置的技巧
- 移动方法或字段:移到更合适的类或源文件中
- 重命名方法或字段:改为更能体现其作用的名称
- 上移(pullup):在 OOP 中,移到基类中
- 下移(push down):在 OOP 中,移到派生类中
- 增强抽象的技术
2.2 通过设计来分解
- 如果使用模块分解,可将以后编写的部分代码放在模块、方法或函数中,程序通常不会像把全部功能放在一起的代码那样密集,结构也更合理
- 当然,仍然建议在编写代码之前设计程序
3. 命名
- 编译器有几个命名规则
- 名称不能以数字开头(例如 9to5)
- 包含两个下划线的名称(例如 my__name)是保留名称,不应当使用
- 以下划线开头(例如 _Name)的名称是保留名称,不应当使用
3.1 选择恰当的名称
3.2 命名约定
-
1. 计数器
- 程序员习惯把 i 和 j 分别用作计数器和内部循环计数器,然而要小心嵌套循环时搞混
- 使用二维数据时,与使用 i 和 j 相比,将 row 和 column 用作索引会更容易
-
2. 前缀
-
3. 匈牙利表示法
- 匈牙利表示法是关于变量和数据成员的命名约定,其基本思想是使用更详细的前缀而不是一个字母 (例如 m) 表示附加信息
-
4. 获取器和设置器
- 如果类包含了数据成员例如 mStatus,习惯上会通过获取器 getStatus() 和设置器 setStatus() 访问这个成员
- 要访问布尔数据成员,通常将 is(而非 get) 用作前缀,例如 isRunning()
-
5. 大写
- 变量和数据成员几乎总以小写字母开头,并用下划线 (my_queue) 或大写字母 (myQueue) 分隔单词
- 函数和方法通常将首字母大写,但是本书采用小写风格的函数和方法,把它们与类名区别开来
- 大写字母可用于为类和数据成员名指明单词的边界
4. 使用具有风格的语言特性
4.1 使用常量
- C++ 语言提供了常量,可以把一个符号名称赋予某个不变的值 (例如 2.71828)
const double kApproximationForE = 2.71828182845904523536;
4.2 使用引用代替指针
- 用引用替换指针有许多好处
- 1、首先,引用比指针安全,因为引用不会直接处理内存地址,也不会是 nullptr
- 2、其次,引用在文体上比指针好,因为引用使用与堆栈变量相同的语法,没有使用 * 和 & 等符号
- 3、使用引用的另一个好处是它明确了内存的所有权
- 如果一个程序员编写了一个方法,另一个程序员传递给它一个对象的引用,很明显可以读取并修改这个对象,但是无法轻易地释放对象的内存。如果传递的是一个指针,就不那么明显
用指针传递未必意味着对象将改变,因为参数可能是 const T*。传递指针或引用是否会修改对象,都取决于函数原型是否使用了 const T*、T*、const T& 或 T&
5. 格式
5.1 关于大括号对齐的争论
- 本书中,除了函数、类和方法名之外,大括号与起始语句放在同一行
void someFunction
{
if (condition) {
cout << "condition was true" << endl;
} else {
cout << "condition was false" << endl;
}
}
5.2 关于空格和圆括号的争论
- 本书在任何关键字之后都会使用空格,在运算符前后都会使用空格,在参数列表或函数调用中的每个逗号之后都会使用空格,并使用圆括号表明操作顺序
if (i == 2) {
j = i + (k / m);
}