part1 让自己 习惯 C++
1 视 C++ 为 1个 语言联邦
1 C++ 是 `5 重范式: Procedural/OO/GP/Functional/MP`
2 C++ 可视为 `4个 子语言: (策略不同) C/OO C++/Template C++/STL`
`[2/3] pass-by-reference-to-const 往往更好`
2 尽量以 const / enum / inline 替换 #define
, 即 优选 compiler
而不是 preprocessor
————————————————————————————————————————————————————————————
3者 相比 #define 的 优势
————————————————————————————————————————————————————————————
(1) const
[1] `编译器 检查`
[2] 可作 `class static 成员数据`
Note: const 独特之处
const object (默认 内部链接) 想具有 `global scope`
-> `定义/初始化 必须放 头文件`
————————————————————————————————————————————————————————————
(2) enum
[1] `防止 通过 ptr/ref 访问 整型常量`
————————————————————————————————————————————————————————————
(3) inline
[1] 无副作用 / type safety
宏 max(a, b) 以 `2个操作数递增` 形式 调用
————————————————————————————————————————————————————————————
3 尽可能 用 const
`1 const + returnType / paraType / mem_func`
——————————————————————————————————————————————————————————————————————————————————————————————————
(1) const + returnType : 预防 `没意思的赋值` 等 client 意外错误
(a * b) = c; // a*b 的 result 上 调 operator=
——————————————————————————————————————————————————————————————————————————————————————————————————
(2) const + paraType : pass by reference to const
——————————————————————————————————————————————————————————————————————————————————————————————————
(3) const mem func : 用于 const object
1) `2个 同名 mem func 的 const/non-const 版本 是 重载`
2) mem func 为 const 的 2 层含义 & mutable
[1] bitwise const: const mem func 不可改变 object 内 任何 non-static 成员 data
|
| 问题: bit const 语义可能不合适
|/
1] bit const 语法对 + 语义不对
指针成员 + 不改指针, 但 通过 `返回 ref` 的 `下标运算符` 改变 `指针 所指位置上的值`
2] bit const 语法不对 + 语义不对
成员 `不算` 对象 `真正状态`
|
| 引出
|/
[2] logical constness: 逻辑含义上 const -> mutable 成员变量 可 修改
——————————————————————————————————————————————————————————————————————————————————————————————————
`2 当 const 和 non-const mem func 实现等价` 时,
`用 const 版本 实现 non-const 版本 以 `避免代码重复`
[1] static_cast 转型 non-const obj 为 const obj
*this 从 C& 转型为 const C&
[2] const_cast 移除 return type (const T&) 中的 const
3 const + ptr/iterator 注意 `const_iterator == 内容 const`
4 object 使用前 要先 初始化
`1 ctor 最好 用 initialization list 初始化`
原因
`[1] 高效性`
`不 显式 写 init. list, compiler 会隐式 填/调 init. list
=> 隐式 ctor 浪费了(=> 效率低), 隐式 built-in 初始化为 undefined value`
`[2] 必要性`
`built-in type 成员变量` 若为 `const 或 reference`
`只能用 init. list 而不能用 assignment`
2点注意
[1] init. list 中要 `列出 所有 mem variable, 遗漏 built-in variable 将导致 undefined behavior`
[2] 多份 init. list 导致 code 重复
assignment 表现 像 initialize 一样好 的 mem data
从 initialization list 移到 assignemnt -> `公共 private func`
`2 初始化次序`
`(1) 不同 编译单元` 内定义的 `non-local static 对象` 初始化 次序 undefined
`问题: 编译单元 1 中 non-local static object 初始化 use 编译单元 2 中 non-local static object 可能 尚未初始化 -> undefined behavior`
|
| 解决: `单例模式`
|/
用 `函数调用` 替换 "直接访问 non-local static object"
2个优势
[1] `所得 ref 指向 已初始化 对象`
[2] `未 调用函数, 就不会引发 构造和析构` 成本, 真正的 non-local static object 可没这等便宜
机制
[1] non-local static object ->替换为 -> local static object + return ref to it
[2] `func 内 的 local static object 会在 "该函数被调用 期间" "首次遇上该对象之定义式" 时 被 初始化`
=== 细节
0 Introduction
(1) 2 类 忠告
————————————————————
[1] 一般 设计策略
[2] 特殊特性
————————————————————
(2) 设计: 多种 chiose 该选哪个?
————————————————————————————————————————————
[1] Inheritance / Template ?
[2] public/private/protected Inheritance ?
[3] Composition / private Inheritane ?
[4] mem func / non-mem func ?
[5] pass by value / pass by reference ?
————————————————————————————————————————————
(3) 棘手 问题
———————————————————————————————————————————————————————————
[1] assignment 适当 return type 是 什么?
[2] dtor 何时该为 virtual?
[3] operator new 无法找到 enough memory 时, 该怎么办 ?
———————————————————————————————————————————————————————————
(4) 准则 天生就有 例外
=> 每个条款 都有说明
(5) 本书目的
tell 如何回避 compiler 难以显露 的问题
(6) 本书用途
彻底了解 C++
————————————————————————————
[1] how 行为
[2] why 那么行为
[3] how 运用其行为 形成优势
————————————————————————————
0.1 术语 terminology
(1) declaration: tell compiler 某物 name 和 type
——————————————————————————————————————————————
4 种 declarations
——————————————————————————————————————————————
[1] object extern int x;
[2] function std::size_t func(int n);
[3] class class A;
[4] template templete <typename T> class B;
——————————————————————————————————————————————
(2) definition: 为 compiler 提供 declaration 所遗漏的 细节
——————————————————————————————————————————————————————————————
[1] object : compiler 为 object 分配内存
[2] function /function template : 提供 code body
[3] class / class template : 列出 member
——————————————————————————————————————————————————————————————
(3) Initialization: 给予 对象初值
用户自定义对象, Initialization 由 ctor 执行
——————————————————————————————————————————————————————————————————————————————————
1) default ctor
无参 / 所有参数都有 缺省值
2) ctor + explicit
阻止 ctor 被执行 implicitly type conversion
禁止 compiler 执行 非预期 的 type conversion
除非 有更好的理由 允许 ctor 用于 implicitly type conversion, 否则 用 explicit
3) copy ctor:
以 同型 object 初始化 自我新对象
4) copy assignment
以 同型 object 初始化 自我已有对象
——————————————————————————————————————————————————————————————————————————————————
class A
{
public:
A(); // default ctor
A(const A& rhs); // copy ctor
A& operator=(const A& rhs); // copy assignment
};
// 调 default ctor
A a1;
// 调 copy ctor
A a2(a1);
A a3 = a1; // note: = 也可 用来 调 copy ctor
// 调 copy assignment
a1 = a2;
(4) STL
容器
vector 等
迭代器
vector<int>::iterator 等
算法
for_each
find
function objects
行为像函数
重载 函数调用运算符 operator() 的 class
(5) 函数 signature: paraType + returnType
std::size_t f(int x); -> std::size_t (int)
(6) interface
[1] Java 中 Interface 为 语言元素
[2] C++ 中 接口 指 一般设计概念
1] 函数 signature
2] class 的 可访问 成员
(7) client 人/物
对 1个 模板/组件, 其 `使用者 -> client`
function / class / template 的 client: 其 caller
(8) undefined behavior
——————————————————————————————————————
2 种导致 undefined behavior 的 case
——————————————————————————————————————
[1] 解引用 nullptr
[2] 指涉 `无效 数组 index`
——————————————————————————————————————
int* p = 0;
std::cout << *p ; // [1]
char name[] = "lilei";
char c = name[10]; // [2]
0.2 命名习惯
(1) lhs / rhs: 形参名
left/right-hand side
对 mem func
左侧实参 由 this 表现, 只用 rhs
(2) 指针/引用 缩写: 只取 `每个单词 首字母`
——————————————————————————————————
[1] pt: 指向 `T 型 对象` 的 指针
|
|/
pointer to T
[2] rw: reference to Widget
——————————————————————————————————
Widget* pw;
class GameCharacter;
GameCharacter* pgc;
0.3 线程 thread
part1 让自己 习惯 C++
1 视 C++ 为 1个 语言联邦
1 C++ 是 `5 重范式`
—————————————————————
[1] Procedural
[2] OO
[3] GP
[4] Functional
[5] Metaprogramming
—————————————————————
2 C++ 可视为 `4个 子语言` (策略不同)
(1) C
preprocessor
block
statement
built-in data type
array
pointer
(2) OO C++
class
encapsulate
inheritance
polymorphism
vf
(3) Template C++
GP / TMP
(4) STL
是 template 程序库
值传递 还是 引用传递 ? (通常情况)
————————————————————————————————————————————————————————————————————————————————
[1] C: build-in type pass-by-value 更高效
[2] OO C++ user-defined 存在 ctor 和 dtor,
pass-by-reference-to-const 往往更好
|\
|
[3] Template C++ 尤其如此, 因为你 `甚至不知道 所处理的 类型`
[4] STL iterator / function object 都是在 C pointer 上 塑造出的
pass-by-value 更高效
————————————————————————————————————————————————————————————————————————————————
2 尽量以 const / enum / inline 替换 #define, 即 优选 compiler 而不是 preprocessor
1 const
(1) 对 纯 `常量`, #define 常量名 常量值 / define Ratio 1.6
问题
常量名 不出现在 symbol talbe
=> 预处理器 移走 `常量名`
=> 编译错误 中的 常量值 很难排错
解决
`const 对象` // 定义(初始化) 通常放 头文件
const double Ratio = 1.6;
——————————————————————————————————————————————————————————————————————————————
(2) #define + const pointer + const content
|
| 替换为
|/
const pointer + const content
+ 想让 `const ptr(也是1种 object) 具有 global scope` (#define 的 本意)
|
|
|/
`定义/初始化 必须放 头文件`
#define NAME "lilei"
|
|/
const char* const name = "lilei";
——————————————————————————————————————————————————————————————————————————————
———————————————————————————————————————————————————————————————————————
Note:
1) const object 想具有 `global scope` -> `定义/初始化 必须放 头文件`
|\
| <=>
|/
被 多源文件使用
原因: const `默认`为 `内部链接`
=>
1] 不能在 other 编译单元 ( 通过 extern 方式 ) 访问
2] 放 `头文件` 被 `多 源文件 #include`,
不会报错 `重定义`
|
|
|/
`外部链接 object` 定义/初始化放 `头文件`
+ .h 被 >= 2 个源文件 #include
=> 重定义
———————————————————————————————————————————————————————————————————————
2) const 对象 `特殊性`
[1] `默认`为 `内部链接`
[2] 用 `extern (显式)声明` 可赋予其 `外部链接`
[3] 其值不可改变 => 必须初始化
[4] 2种初始化: 编译期 / 运行期初始化
———————————————————————————————————————————————————————————————————————
(3) #define 无法创建 `class static 成员数据`, 不能提供 封装`
|
| [1] 例外
|/
为 `整型(int char bool)`
+ `不取地址`
=> 可
————————————————————————————————————
// 头文件
1] `类 内 带初值 的 声明`, 而非定义
2] `类内 使用` 而不用提供 定义式
// 实现(源)文件
3] `类外定义式 + 初值 从 声明式获取`
————————————————————————————————————
// 头文件
class A
{
private:
static const int N = 5; // 带初值 的 声明, 而非定义
int arr[N]; // 类内 使用
};
// 实现(源)文件
const int A:N; // 类外定义式 + 初值 从 声明式获取
[2] 非例外: class static 成员数据 `初值 必须放 定义式`
// 头文件
class B
{
private:
static const double Factor; // 声明 头文件
};
// 实现文件
const double B::Factor = 1.5; // 定义, 放 实现文件
|
| 该 情形下, 若想在 `编译期获` 得 `class 内 常量值`
|/
solution: the enum hack
2 enum hack
class A
{
private:
enum {N = 5}; // the enum hack
int arr[N];
};
(1) enum hack 更像 #define 而不是 const
enum/#define 取地址 不合法
=> `防止 通过 ptr/ref 访问 整型常量`
(2) `enum 类型 值 可当 int 用`
(3) enum hack 是 TMP 基础技术
3 template inline func 代替 宏
(1) 宏 像 函数, 却 `不招致` 函数调用 带来的 `额外开销`
// 以 a b 较大值 调 func
#define call_with_max(a, b) func( (a) > (b) ? (a): (b) )
(2) `宏 中 所有实参 即使加 小括号, 也可能有问题` // 无聊事
int a = 5, b = 0;
call_with_max(++a, ++b); // a 递增
int a = 0, b = 5;
call_with_max(++a, ++b); // b 递增
|
| solution
|/
template inline func 兼顾 效率 + type safety
// 不知 T 是何 type, 所以用 pass by reference to const
template <typename T>
inline void call_with_max(const T& a, const T& b)
{
f(a > b ? a: b);
}
3 尽可能 用 const
1 const + returnType / paraType / mem_func
(1) const + returnType : 预防 `没意思的赋值` 等 client 意外错误
(a * b) = c; // a*b 的 result 上 调 operator=
(2) const + paraType : pass by reference to const
(3) const mem func : 用于 const object
1) `2个 同名 mem func 的 const/non-const 版本 是 重载`
2) mem func 为 const 的 2 层含义 & mutable
[1] bitwise const: const mem func 不可改变 object 内 任何 non-static 成员 data
|
| 问题: bit const 语义可能不合适
|/
1] bit const 语法对 + 语义不对
指针成员 + 不改指针, 但 通过 `返回 ref` 的 `下标运算符` 改变 `指针 所指位置上的值`
2] bit const 语法不对 + 语义不对
成员 `不算` 对象 `真正状态`
|
| 引出
|/
[2] logical constness: 逻辑含义上 const -> mutable 成员变量 可 修改
// 1)
class Text
{
public:
// const 版本: 用于 const object
const char& operator[] (std::size_t pos) const { return text[pos]; }
// non-const 版本: 返回类型 是 reference to char, 不是 char, 否则, tb[0] = 'x' 编译不过
char& operator[] (std::size_t pos) { return text[pos]; }
private:
std::string text;
};
void print(const Text& ctb)
{
std::cout << ctb[0]; // const 对象 => 调 const 版本
ctb[0] = 'x'; // 写 const char& => error
}
Text tb("hello");
std::cout << tb[0]; // non-const 对象 => 调 non-const 版本
// 2-1-1) bit const 语法对 + 语义不对
class CText
{
public:
char& operator[] (std::size_t pos) const { return text[pos]; } // bitwise const 声明, 但不恰当
private:
char* ptext; // 与 C API 沟通
};
const CText ctb("hello");
char* pc = &ctb[0];
*pc = 'I';
// 2-1-2) bit const 语法不对 + 语义不对
class CText
{
public:
std::size_t length() const;
private:
char* ptext;
std::size_t textLength;
bool lengthValid;
};
std::size_t
CText::length() const
{
if( !lengthValid )
{
textLength = std::strlen(ptext); // error, const mem func 内
lengthValid = true; // 不能 改 non-static mem data
}
}
// [2]
class CText
{
public:
std::size_t length() const;
private:
char* ptext;
mutable std::size_t textLength;
mutable bool lengthValid;
};
std::size_t
CText::length() const
{
if( !lengthValid )
{
textLength = std::strlen(ptext);
lengthValid = true;
}
}
`2 当 const 和 non-const mem func 实现等价` 时, `用 const 版本 实现 non-const 版本 以 `避免代码重复`
[1] static_cast 转型 non-const obj 为 const obj
*this 从 C& 转型为 const C&
[2] const_cast 移除 return type (const T&) 中的 const
class Text
{
public:
const char& operator[] (std::size_t pos) const { return text[pos]; }
char& operator[](std::size_t pos) { return text[pos]; }
private:
std::string text;
};
|
| const 版本 实现 non-const 版本
|/
class Text
{
public:
const char& operator[](std::size_tpos) const { return text[pos]; }
char& operator[](std::size_t pos)
{
return
const_cast<char&>(
static_cast<cosnt Text&>(*this)
[pos]
);
}
private:
std::string text;
};
3 const + ptr/iterator
[1] const T* = T const *
[2] Note
const std::vector<int>::iterator // 迭代器 const
std::vector<int>::const_iterator // 内容 const
4 object 使用前 要先初始化
`1 ctor 最好 用 initialization list 初始化`
class Phone{ ... };
class ABEntry // "Address Book Entry"
{
public:
ABEntry(const std::string& addr_,
const std::list<Phone>& phones_);
private:
std::string addr;
std::list<Phone> phones;
int num;
};
ABEntry::ABEntry(const std::string& addr_,
const std::list<Phone>& phones_)
{
addr = addr_;
phones = phones_;
num = 0;
}
// 等价于
ABEntry::ABEntry(conststd::string&addr_,
conststd::list<Phone>&phones_)
: addr(), phones(), num(undefined_val)
{
addr = addr_;
phones = phones_;
num = 0;
}
// 等价较佳写法: 效率较高
ABEntry::ABEntry(conststd::string&addr_,
conststd::list<Phone>&phones_)
:addr(addr_), // 调 copy ctor
phones(phones_),
num(0)
{ }
// init. list 中 可 default 构造: nothing 作 arg
ABEntry::ABEntry(conststd::string&addr_,
conststd::list<Phone>&phones_)
:addr(), // 调 default ctor
phones(),
num(0)
{ }
`(1) 总是用 init. list: 有时 绝对必要, 又往往比 assignment 更高效`
`(2) built-in type 成员变量` 若为 `const 或 reference, 只能用 init. list 而不能用 assignment`
built-in type 成员变量 non-const / non-reference 时,
init. list / assignment 均可用, 且 成本相同
class A
{
public:
A(): n(5), name("") {}
A(int N, std::string& name_)
: n(N), name(name_) { }
private:
const int n;
std::string& name;
};
`(3) init. list 中 要列出 所有 mem variable, 遗漏 built-in variable 将导致 undefined behavior`
ABEntry::ABEntry(conststd::string& addr_,
conststd::list<Phone>& phones_)
// or // :addr(), phones()
{ }
// 等价于
ABEntry::ABEntry(conststd::string& addr_,
conststd::list<Phone>& phones_)
: addr(), phones(), num(undefined_val)
{ }
`(4) 多份 init. list 导致 code 重复 -> assignment 表现 像 initialize 一样好
的 mem data 从 initialization list 移到 assignemnt -> 公共 private func`
尤适于`成员变量 初值 由 文件 或 数据库 读入`
`2 初始化次序`
(1) 5 种 static 对象
file scope 内
namespace scope 内
global
class 内
func 内
// your file
class FileSystem
{
public:
std::size_t nunDisks() const;
};
extern FileSystem tfs; // 给 client 使用的 对象
// client file
class Directory
{
public:
Directory( param );
};
Directory::Directory( params )
{
std::size_t disks = tfs.numDisks(); // 使用 fs 对象
}
Directory tempDir( params );
|
| 单例模式
|/
//
class FileSysten { ... };
FileSystem& tfs()
{
static FileSystem fs;
return fs;
}
//
class Directory { ... };
Directory::Directory (params)
{
std::size_t disks = tfs().numDisks();
}
Directory& tempDir(params)
{
static DIrectory td(params);
return td;
}
tempDir(params)
(2) `引用返回` 函数 可 inline
而 `non-const static 对象, 不论 local 还是 non-local, 多线程 下 "等待某事发生" 都有麻烦`
|
| 解决
|/
`单线程启动阶段 (single-thread startup portion) 手工调所有 reference 函数`,
可 `消除 初始化` 相关的 `race condition`
`set_new_handler`
```
new_handler 是个 function pointer,
所指 函数 无参 / 无 return_value
set_new_handler:
para
new_handler: 指向 operator new 无法分配 足够内存时, 该被调用的函数
return value
new_handler: 指向 set_new_handler 被调用前 的 new_handler 函数
(3) base class 先于 derived class 初始化
(4) class mem data 以其 声明顺序 初始化
3 build-in / 之外, 要手工初始化 / 由 ctor 初始化
原因
C / non-C part of C++: C++ 不保证 / 保证 初始化 它们
eg: array / vector
网友评论