第1章 C++ 设计原则 / 对 C & C++ 程序员的建议
1.1 C++ 设计 2 个原则
(1) 不给比 C++ 更底层的语言 留任何余地
(2) 零开销原则/ZOP
与等价的 替代方法相比, 不浪费 1个字节(空间) 或 1个CPU时钟周期(时间)
1.2 建议
1 对 C++ 程序员的建议
用 C++11 新特性, 使之 更现代化
(1)
用 ctor 建立 不变式
配合 ctor/dtor 简化 资源管理(RAII)
(2) 优先用... 而非...
——————————————————————————————————————————————————————
容器和算法 内置数组 和 专用代码
——————————————————————————————————————————————————————
标准库 自己开发的代码
——————————————————————————————————————————————————————
异常 错误代码 来报告 不能局部处理的错误
——————————————————————————————————————————————————————
(3) smart ptr
————————————————————————————————————————————————————————————————
避免裸的 new/delete
————————————————————————————————————————————————————————————————
用 unique_ptr 引用 多态对象
————————————————————————————————————————————————————————————————
用 shared_ptr 引用 共享对象, 即 不止1个 owner 负责其析构 的对象
————————————————————————————————————————————————————————————————
(4) 用 移动语义 来避免 copy 大对象
(5) 用 模板 来保持 静态类型安全(消除 类型转换), 并 避免 类层次的 不必要使用
2 对 C 程序员的建议
(1) 想发挥 C++ 相对于 C 的优势, 要采用 `(与 C) 不同的 设计&实现 风格`
学 C++ 标准库 -> 与 C标准库 diff
字符串 copy/cmp 用 =/== 而不是 strcpy()/strcmp() 等
(2) 宏: C++ 几乎从来不用 -> 替代
1) 定义 explicit 常
const / constexpr / enum (class)
2) 避免 函数调用开销
inline
3) 指明 函数族/类型族
template
4) 避免 名字冲突
namespace
(3) 优选... 而不是...
new/vector malloc()/realloc()
std::vector 数组
std::string C 风格字符串
(4)
避免用 void* / union / 类型转换, 除非在 某些函数或类的 深层次实现中
否则
1) 会限制你从 类型系统 得到的 支持
2) 通常, 1次 类型转换 就暗示着 1个 设计错误
若 必须用 显式类型转换, 优选 命名的 显式类型转换(static_cast 等), 更准确地 表达意图
(5) 真正需要 变量时, 再声明 并 立即初始化
(6) 不要认为 用 C 风格辛苦写出的程序会 比 简短的C++替代程序(如使用 标准库) 更高效
实际上, 通常正好相反
第2章 C++ 概览: 基础知识
2.1 基本概念
1 类型 / 初始化
(2) 初始化方式
1) 列表 {}
1) = 可选
2) 确保不会发生 窄化类型转换
2) =
= 配合 auto
不存在 可能发生错误的 类型转换
何时该用 auto?
无明显理由 需 显式指定类型
2 2种 不变性 / 常量 / 常量表达式 / constexpr 函数
(1) 3个难点
1)
常量 : 初始化(编译时/运行时) 之后, 值不会再改变
常量表达式: 编译时能求值
2)
const 常量: 可在 `运行时求值`
/| |
必为 | | 未必为
| |/
cosntexpr 常量: `编译时能求值`
3) cosntexpr 函数
实参 为 常量表达式 : 编译时求值 => 结果 (是常量表达式) 可用于 常量表达式
实参 为 非常量表达式: 运行时求值 => 结果不可用于常量表达式
(2) C++ 支持 `2种 不变性`
——————————————————————————————————————————————————————————————————————————————
| const | constexpr
——————————————————————————————————————————————————————————————————————————————
| | 1) 对象定义
含义 | (编译器)承诺不改变这个值 | 编译时求值(若求不出 => 报错)
| | 2) 函数定义
| | 若 实参 为 常量表达式/else,
| | 则 函数 应该能/不能 用在 常量表达式
——————————————————————————————————————————————————————————————————————————————
| 说明`接口` |
主要用于| | 1) 数据 放 只读内存 / 提升性能
| | 2) ...
——————————————————————————————————————————————————————————————————————————————
constexpr double res1 = square(v1);
/
/ 1) 若 square(10) 是 常量表达式, 则 正确
// 常量
cosnt int v1 = 10;
\ 2) 与 用 constexpr 含义等价
\
const double res2= square(v1);
constexpr double res3 = square(v2);
/
/
/ 3) error!!! : var2 不是 常量表达式
// 非常量 /
int v2 = 10;
\
\ 4) ok: 运行时 求值
\
cosnt double res4 = square(v2);
5) double sum(const vector<double>&); // sum 不更改 其 参数的值
(3) 常量表达式 应用
1) 语言规则需要
1> 数组 size
2> case 标签
3> constexpr 声明的 常量
4> (某些) 模板实参
2) 不变性 (对象状态 不发生改变)
3) 编译时求值:性能高
new 运算符 分配内存的区域
自由存储 / 动态内存 / 堆: 3 者含义相同
2.3 模块化
模块
函数 / 用户自定义类型 / 类层次 / 模板
构建C++ 程序的关键
模块间: 清晰定义交互关系
某模块:接口与实现 分离
模块化
逻辑上
通过 语言特性 描述模块
物理上
通过 划分文件 和 分离编译 分离/组合模块
1 分离编译
(1) 含义
user 代码只能看见所用 类型和函数 的 声明
它们的 定义 放 分离的 源文件, 并 `分别编译`
(2) 优点
1) 编译时间 降到最少
2) 逻辑独立的部分 分离 => 错误几率 降到最低
(3) 应用
库: 分离编译的 代码片段 的 集合
(4) 机制
模块头文件:
模块(Vector) 接口
/ \
/ \
用户源文件: 模块源文件:
使用 Vector 定义/实现 Vector
// 1) Vector.h
class Vector
{
public:
Vector(int s);
double& operator[](int);
int size();
private:
double* elem;
int sz;
};
// 2) user.cpp
#include "Vector.h"
#include <cmath>
using namespace std;
double sqtr_sum(Vector& v)
{
// ...
}
// 3) Vector.cpp
#include "Vector.h" // 获得接口
Vector::Vector(int s)
: elem {new double[s] }, sz(s) { }
double& Vector::operator[](int i) { return elem[i]; }
int Vector::size() { return sz; }
2 名字空间
表达一组声明 属于 一个整体
避免 `名字冲突`
3 错误处理
(1) 异常
1) 机制
分离 运行时 错误捕获位置 与 错误处理位置
2) 可 自定义 异常类
3) 例子
Vector 实现者 `检测` 异常, 通过 throw `通知` 使用者
Vector 使用者 通过 catch 采取应对措施
// 实现者
double& Vector::operator[](int i)
{
if(i<0 || size() <= i)
throw std::out_of_range("Vector::operator[]");
return elem[i];
}
// 使用者
void f(Vector& v)
{
try
{
v[v.size()] = 10;
}
catch(std::out_of_range)
{
// 处理
}
}
(2) `类的不变式`
1) 含义
假定某事为真的 声明
2) 建立 类的不变式
1> 是 Ctor 的责任, 从而 成员函数 可以依赖于 不变式
2> 确保 成员函数退出时, 不变式 仍成立
Vector::Vector(int s)
{
if(s <0)
throw length_error{};
elem = new double[s];
sz(s);
}
网友评论