(1) 对象 作 `异常` 也可能会被 copy/move
(2) 类有 `引用/指针成员`, 可能需要 `copy / dtor + 非默认 copy`
17.1 ctor 和 dtor
1 ctor
(1) 必须建立 `类的不变式`: 成员函数 (从 类外) 被调用时, 必须保持的 something
(2) 显式调用
placement new
void C::push_back(const X& a)
{
new(p) X {a};
}
2 dtor
(1) 阻止析构
应用: 阻止 隐式销毁, 只允许 显式销毁
[1] = delete
[2] 放 private -> 更 灵活
class Nonlocal
{
public:
void destory() { this->~Nonlocal(); } // 显式析构
private:
~Nonlocal(); // 不能 隐式析构
};
void user()
{
Nonlocal x; // error
Nonlocal* p = new Nonlocal; // ok
delete p; // error
p->destory(); // ok
}
(2) 显式调用
void C::pop_back()
{
p->~X();
}
(3) 隐式调用
1] scope 变量 离开 scope
2] delete 动态分配的指针
(4) virtual dtor:
若 `dtor 非虚` => delete 多态指针 pBase 调用的是 `Base dtor`
1) Derived 对象 own 的 `resource 泄露`
2) Derived 对象 本身仅 basePart 析构
17.2 初始化
类对象
1 不使用 ctor 的 case: {} 列表初始化: 逐成员
初始化
vector<int> v1{10}; // 用 值 10 初始化 1个元素
vector<int> v2(10); // Note: 调 Ctor, 将 10个元素 初始化为 0
2 默认 ctor
(1) 内置类型
1) `非静态分配 (local non-static / 自由存储 + not 列表初始化 {} )` 变量
`不初始化`
2) `静态分配`
调 默认 ctor 进行初始化
————————————————————————
默认值
整型 0
浮点 0.0
指针类型 nullptr
————————————————————————
void f()
{
int* p1 = new int; // 未初始化
int* p2 = new int{}; // 自由存储 + 列表初始化 {} => 初始化 为 0
}
17.3 初始化 成员和基类
1 ctor 会 初始化其 (直接) 基类 + 成员
2 成员 初始化 vs 赋值
1) 必要性: `引用成员 / const 成员` 必须初始化
2) 性能优势
3 static 成员: 类内初始化
的 条件
————————————————————————————————————————————————
const + 整型 / 枚举类型
————————————————————————————————————————————————
字面值类型 的 constexpr + 初始化器 是 常量表达式
————————————————————————————————————————————————
template<class T, int N>
class A
{
public:
static const int c = 1;
static constexpr int max = N;
// ...
private:
T a[max];
};
17.4 copy & move
1 copy
(1) copy ctor 应确保 copy 了 基类和成员: 从 copy 目的 看, 基类 就是 成员
(2) copy virtual 基类
[1] 初始化义务 被 "冒泡" 到 最终派生类: 默认 copy ctor 能正确 copy 它
[2] `自定义 copy ctor: 重复 copy ( 可能 更高效)
(3) `浅 copy`
|
| 令 `2 个对象` 进入
|/
1) `共享状态` => `对象纠缠`
|
| lifetime 问题
|
2) shared_ptr: 好管理, 但并 `没有完全解决` 对象纠缠
[1] `更新`: 谁负责 ? 如何 进行 ? 何时 进行 ?
[2] `多线程`: 需要 `同步` 机制 吗 ? 如何确认 ?
|
|/
3) `不可变 的 共享状态` -> FP
|
|/
4) 写前 copy / COW (copy on write)
思想:
共享状态 被 修改前, 副本并不真的需要 独立性
=> 可 `延迟 共享对象 的 copy`, `直到 修改副本前` 才 `真正进行 copy` -> `修改 副本`
不是万能灵药, 但能
有效地 `结合` `真 copy 的 简单性` 与 `浅 copy 的 性能`
(4) copy ctor & copy 赋值 的 `区别`
————————————————————————————————————————————
copy ctor copy 赋值
————————————————————————————————————————————
X(const X&) X& operator=(const X&)
原始内存 要处理 dstObj own resource
————————————————————————————————————————————
`copy 赋值` 抛出异常 -> 基本保障 / 强保障
基本保障
目标对象 = 旧值和新值 `混合状态`
强保障
先创建 `副本` -> 再 `移动交换 (未使用 赋值)` 内容
A& A::operator=(const A& rhs)
{
A tmp{rhs};
swap(tmp, this); // move 交换
return *this;
}
2 move
(1) 交换
2个 对象值
// version1: 3 个对象 + 3 次 copy
template<class T>
void swap(T& a, T& b)
{
const T tmp = a;
a = b;
b = tmp;
}
|
| 大对象 => 代价高
| `只想 交换 一对值`, 不想 copy
|/
// version2: 3 个对象 + 3 次 move
template<class T>
void swap(T& a, T& b) // "几乎是" 完美的
{
T tmp = std::move(a); // move ctor
a = std::move(b);
b = std::move(tmp);
}
(2) move 不能 抛出异常
-> copy 能抛出异常: 因为 copy 可能需要 获取资源
(3) `compiler 如何知道` 它可以 `优先使用 move 语义`
1) 一侧 `右值` => `语言规则` 告知
2) std::move(x) 返回 `左值实参 x 的 右值引用`
(4) move 后 `源对象` 处于 `可 析构 & 赋值` 状态 (如 "空")
(5) 标准库具有 move 语义的
1) 类型: 容器 / unique_ptr / pair
2) 操作: insert() / push_back(): 接受 `右值引用` 的 version
17.5 (compiler)生成 默认操作
1 默认会生成, 只要 code 用到相应操作; coder 显式自定义则 禁止生成
——————————————————————————————————————————————————————————————
coder 显式自定义 | 禁止生成
——————————————————————————————————————————————————————————————
ctor (含 copy ctor) | 默认 ctor
——————————————————————————————————————————————————————————————
copy / move 操作 | 相应版本
——————————————————————————————————————————————————————————————
dtor | `copy ctor 和 move ctor`
——————————————————————————————————————————————————————————————
显式 dtor => 禁止默认 copy ctor -> 若需要 copy ctor: 显式(自定义)
copy
需禁止 : 类 是 基类 & copy from Derived 被禁止
需自定义: 类 含 ptr -> 默认 copy 逐成员(指针) copy -> Dtor 中重复 delete -> undefined
dtor
需自定义: 类含 ptr -> 需 释放 resource
默认操作: 逐成员 copy/默认构造
2 资源不变式
1) 单参数 Ctor: 给定 newed 的 resource ptr => `禁止了 默认 Ctor`
2) 显式 dtor: delete 资源
3) 显式 copy ctor
3) 解引用 * ->: 访问 resource
template <class T>
class Handle
{
T* p;
public:
Handle(T* p_): p{p_} {}
// 显式 dtor => 禁止默认 copy ctor -> 若需要 copy ctor: 显式(自定义)
~Handle() { delete p; }
T& operator*() { return *p; }
Handle(const T& rhs)
: p { new T{*a.p} } {} // clone
};
void f()
{
Handle<int> h { new int{10} }
Handle<int> h1; // error: 无 默认 ctor
Handle<int> h2(h); //
}
3 delete
(1) 禁止切片
copy (from Derived) delete
Base(const Derived& rhs)
(2) 控制在哪里(stack 还是 heap)分配类对象
dtor delete
obj 禁止在 stack 分配
obj 可 new, 但不能 delete:
因为 delete 会调 dtor, 而 detor 又被 deleted 了 -> 解决 -> dtor 放 private & p 调 public destory()
operator new delete & 没有 global operator new
obj 禁止在 heap 分配
obj 可在 stack 分配
class NotOnStack
{
~NotOnStack() = delete;
};
class NotOnFreeStore
{
void* operator new(size_t) = delete;
};
void f()
{
NotOnStack v1; // error: 不能销毁
NotOnFreeStore* p = new NotOnFreeStore; // error
NotOnFreeStore v1; // ok
NotOnStack* p = new NotOnStack; // ok
}
|
|/
#include <iostream>
class NotOnStack
{
public:
void destory()
{
delete this; // 效果 <=> delete p;
}
private:
~NotOnStack()
{
std::cout << "dtor\n";
};
};
int main()
{
NotOnStack* p = new NotOnStack;
p->destory();
}
4 = default: 显式声明默认操作
compiler 不再了解 函数语义 => 不再 优化
5 部分不变式
自定义 copy assignment + 魔数下标 -> 不安全: 没检查是否满足不变式就访问 -> 解决: 用 默认版本
A& A::operator= (const A& rhs)
{
for(int i = 0; i < 9; ++I)
vec[i] = rhs.vec[i]
}
6 copy move dtor 逻辑上如何联系?答: 保持类的不变式
———————————————————————————————————————————————————————————————
Ctor : 建立 ( `获取资源`)
Copy 和 Move 操作: 保持
Dtor : 清理工作( 含 `释放资源` )
———————————————————————————————————————————————————————————————


网友评论