1 know C++ 默认 生成
并 call 哪些 functions
1 empty class 何时不 empty ?
答: C++ compiler 隐含自动生成 4 大函数
`只有当 这些函数 被调用` 时, `才会被 compiler 创建出来`
=> 此时 empty class 不再 empty
2 compiler 拒绝 implicitly
自动生成 赋值运算符
的 case
`[1] class 内含 reference 成员 / const 成员`
原因
C++ `不允许`
`reference 改 指 不同对象 / const 成员 被修改`
`[2] base class 的 assignmenat 是 private`
2 若 不想 use compiler 自动生成
的 func, 就 明确拒绝
1 compiler implicitly 生成 的 4 大 函数 ( 均 public ) , 如何 阻止编译通过?
本节只 考虑 `copy ctor / assignment`, 对 default ctor 和 dtor 分析同理
思路: 阻止其 implicitly 生成
3 方法:
`(1) explicitly 声明 + 定义为 private 版本`
| |
| |
|/ |/
阻止 compiler [1] client 调用 => compile 不通过
implicitly 生成 [2] member func 和 friend func 调用 => success
=> not safe
`(2) 只 声明 ( compile 通过 ) + 不 定义 ( link 不通过 ) -> 调用 -> linkage error`
`(3) 链接期 错误 转移至 编译期`
private 声明` 提取 到 设计的 `base class
`Uncopyable 作 base class` + ( public 甚至 `private` ) `继承`
|
|
|/
任何 client(mem func/friend func), 尝试 copy / assignment
-> `compiler 尝试 生成 copy ctor / assignment`
-> `尝试 调相应 `base class` 中 对应 func
-> `调用 被 compiler 拒绝`
3 多态 base class
要声明 virtual dtor
1 base class 设计是为了 多态 时, 才需要 virtual dtor
`某些 base class 的 设计并不是为了 多态, 则 不需要 virtual dtor`
如 `Uncopyable`
2 derived object
经由 base class 指针 被 delete
+ base class 的 dtor 非虚
=> undefined behavior
通常是
`只 调 base class 的 dtor => 仅 derived obj 的 base part 被 销毁`
[1] `局部销毁 的 object`
[2] derived 类管理的 `资源泄漏`
|
| 解决
|/
base class 要用 virtual dtor
`=> 对任何 不带 virtual dtor 的 class ( std::string + STL 容器 vector / list 等 ), 不要企图去 继承 它`
3 vf 目的
允许 `derived class 实现` 得以 `客制化`
————————————————————————————————————————————————————————————
`[1] class 含 vf`, 几乎确定 `应该有 1个 virtual dtor`
————————————————————————————————————————————————————————————
`[2] class 不含 vf`, 通常表示 它 `不企图被用作 base class`
————————————————————————————————————————————————————————————
|
|
|/
`令 dtor 为 virtual 是个馊主意`
对象
内存 增加,
=> 本不用 virtual 还可以 传给 C 函数
用 virtual 却不能 传给 C 函数
4 别让 异常 逃离 dtor
[1] dtor 绝不要 抛出异常
`dtor 所调 函数 f()` 可能 `抛出异常` 时,
`dtor 应 捕捉` 异常, 并 `吞下 ( 不传播 ) 异常` 或 `结束程序`
[2] client
若想 处理异常
则 class 应该 `provide a public interface 去 `调 函数 f()`
// 例: DBConn 类 管理 DBConnection
5 ctor/dtor
期间 Never 调 vf
1 derived class object 的 base part 构造/析构 期间, object 的 type 是 base class 而非 derived class
[1] vf 被 compiler 解析至 base class
=> ctor/dtor 中 调 vf, 达不到 预想的 (多态) 结果
|
|/
vf 调 dynamic_cast / typeid 想 达到多态效果
=> ctor / dtor 调用链 中 不要含 vf`
[2] RTTI
dynamic_cast / typeid 视 object 为 base class type
`2 base class func 想 处理 Derived class 的 static mem data, 怎么办?`
|
|/
LogInfo: 不含 未初始化 non-static mem data
`无法 用 vf 从 base class 向下 调用`
调 Base ctor( 引用传递 )`
/ |\
`Derived ctor` | 作 实参
\ |
调 自身 private static mem func` 返回 LogInfo
6 令 operator=
返回 reference to *this
1 为实现 连续赋值
```
operator= 必须 return a ref to lhs 实参: operator= 为 class mem 时, lhs 实参 为 *this`
```
7 在 operator=
中 处理 自我赋值
1 Note `别名` 引起的 `隐晦/潜在 的 自我赋值`
|
|
|/
[1] 指针 / 引用
[2] 指针 / 引用 + 类层次
[1] *px = *py; // px 和 py 可能指向 同一 obj 时
[2] void f(const Base& rb, Derived* pd); // rb 和 *pd 可能指向 同一对象
2. `保证 self-assignment-safe` 的 3 种方法
`(1) [1] 等价 test
[2] delete (原) internal ptr
[3] (新) internal ptr 指向 copy of rhs's pointed content`
`(2) [1] record (原) internal ptr
[2] (新) internal ptr 指向 copy of rhs's pointed content
[3] delete (原) internal ptr`
`(3) copy and swap`: 类 提供 swap 成员函数
8 Copy 对象时 勿忘其 每个 part
1 copying 函数
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
| 若没 `explicitly 传参` 调 `base class 相应函数
copying 函数 |
| 默认
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
copy ctor | implicitly 调 `Base class 某个 default ctor` 去 初始化 Derived object 的 base part`
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
copy assignment | 不修改 base part mem data
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
问题 | 默认操作可能 `潜藏危机`
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
解决 | 显式 调 Base part 的 适当函数
|
| // 继承: copying 函数 调 base class 适当 函数
| D::D(const D& rhs)
| : B(rhs),
| derivedPart(rhs.derivedPart) {}
|
| D& D::operator=(const D& rhs)
| {
| B::operator=(rhs);
| derivedPart = rhs.derivedPart;
| return *this;
| }
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
2 copy ctor / assignment 相同 code` 段 `去重`
不应该 让两者中一个调另一个,
而应该 提供1个 private common func 给两者调
=== 详细
1 know C++ 默认 生成 并 call 哪些 functions
`1 empty class 何时不 empty ?`
若 class 没 explicitly 声明, compiler 会为 class implicitly 自动生成
1个 copy ctor
1个 copy assignment
1个 dtor
若 class 没 explicitly 声明 任何 ctor, compiler 会 implicitly 自动生成
1个 default ctor
class Empty {};
<=>
class Empty
{
public:
Empty() { ... }
Empty(const Empty& rhs) { ... }
~Empty() {...} // 若 Empty 的 base class dtor 为 virtual,
// 则 此处 Empty dtor 也 virtual
Empty& operator=(const Empty& rhs){ ... }
};
2 4 大函数 做什么
(1) default ctor 和 dtor
调 base class 和 non-static 成员函数 的 default ctor 和 dtor
(2) copy ctor 和 copy assignment
将 源对象 的 每个 non-static 成员变量 copy 到 目标对象
`1) copy assignment 与 copy ctor 行为` 通常 `如出一辙`
template <typename T>
class A
{
public:
A(const char* name_, const T& value_);
A(const std::string& name_, const T& value_);
private:
std::string name;
T value;
};
// "xian" : const char*
A<int> a1("xian", 10); // 调 A(const char* name_, const T& value_);
A<int> a2(a1); // 调 copy ctor
`2) compiler 拒绝 implicitly` 自动生成 `赋值运算符` 的 case
`1> class 内含 reference 成员 / const 成员`
原因
C++ `不允许`
`reference 改 指 不同对象 / const 成员 被修改`
template <typename T>
class A
{
public:
A(const std::string& name_, const T& value_);
private:
std::string& name; // reference
const T value; // const
};
std::string addr1("xian");
std::string addr2("xianyang");
A<int> a1(addr1, 10);
A<int> a2(addr2, 8);
a1 = a2;
`2> base class 的 assignmenat 是 private`
2 若 不想 use compiler 自动生成
的 func, 就 明确拒绝
`1 compiler implicitly 生成 的 4 大 函数 ( 均 public ) , 如何 阻止编译通过?`
class Uncopyable
{
protected:
Uncopyable() {}
~Uncopyable() {} // 非必须为 virtual
private:
// 只声明 + 不定义
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
class HomeForSale: private Uncopyable // 非必须为 public 继承
{
... // 不再声明 copy ctor / assignment
};
1) Uncopyable 实现 的 `微妙之处:`
[1] 不一定非得 public 继承
[2] dtor 不一定非得 virtual
2) Uncopyable 不含 data
`empty base class optimization`
`Uncopyable 作 base class` 被继承 的 技术 可能导致
`多重继承`
|
| 有时会
|/
`阻止` empty base class optimization
通常 可忽略这些 微妙点, 只像 上面那样 使用 Uncopyable
2 非 compiler 自动生成 函数, 如何阻止 编译通过?
class 内 不 explicitly 声明
3 不可 copy / assignment 的 东西: `独一无二`
如 要卖的房子
class HomeForSale { ... }
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); // 企图 调 copy ctor : 不该 编译通过
h1 = h2; // 企图 调 assignment: 不该 编译通过
3 多态 base class
要声明 virtual dtor
`1 base class 设计是为了 多态 时, 才需要 virtual dtor`
`2 derived object` 经由 `base class 指针 被 delete` + `base class 的 dtor 非虚`
=> undefined behavior
`3 vf 目的`
允许 `derived class 实现` 得以 `客制化`
`4 若希望拥有 abstract class, 但 手上没任何 pure vf`
|
| 解决
|/
`pure virtual dtor`
4 别让 异常 逃离 dtor
`1 STL 容器 所放 class 的 dtor 可能 抛出异常` 时, 程序可能 过早结束 或 `undefined behavior`
class Widget
{
public:
~Widget() {} // 假设这里可能 抛出异常
};
void dosth()
{
std::vector<Widget> v;
} // v 自动销毁
`2 若 dtor 中 动作 f() 可能 抛出异常, 该怎么办?`
`答: 将 调 f()` 的 `责任` 从 dtor `转移` 到 client
`1) undefined behavior 没转移, 而是 限制了`
`dtor 抛出 异常`, 会带来 `undefined behavior`
而 `client` 可 `处理异常`
2) 2处 调 f() 并不违反 `让接口容易被正确使用`
1> 供 client 的 f()
让 client 间接调 f(),
并 `对异常` 做出相应 `处理`
2> dtor 中 f(),
`不处理异常`
只是 在 发生异常时, 结束程序 或 吞下异常
class DBConnection // [1] 负责 数据库连接 的 class
{
public:
static DBConnection create(); // return DBConnection object
void close();
};
class DBConn // [2] 用于 管理 DBConnection object 的 class
{
public:
~DBConn()
{
db.close();
}
private:
DBConnection db;
};
// client
{
DBConn dbc(DBConnection::create() );
} // DBConn object 销毁 -> 调 DBConn dtor -> 调 DBConnection 的 close()
`DBConn dtor 调 DBConnection close() 时,发生异常, 则 DBConn dtor 会 传播该异常 ( 允许 异常 离开 DBConn dtor ) -> 抛出 难以驾驭的 麻烦: undefined behavior`
(1) 解决1: `抛出异常时, 结束程序`
`dtor 中 抛出异常` -> `std::abort() 强迫结束程序` 是 `合理选择`
~DBConn()
{
try
{
db.close();
}
catch(...)
{
log close 调用失败;
std::abort();
}
}
(2) 解决2: `吞下 异常`
~DBConn()
{
try
{
db.close();
}
catch(...)
{
log close 调用失败;
}
}
前2个 solution 都 `没什么吸引力`, 因为 都 `无法处理 抛出的异常`
较佳策略:
(3) 解决3
`将 调 close()` 的 `责任` 从 DBConn dtor `转移` 到 DBConn client
`1) undefined behavior 没转移, 而是 限制了`
`dtor 抛出 异常`, 会带来 `undefined behavior`
而 `client` 可 `处理异常`
2) 2处 db.close() 并不违反 `让接口容易被正确使用`
1> 供 DBConn client 的 close()
让 DBConn client 间接调 db.close(),
并 `对异常` 做出相应 `处理`
2> dtor 中 db.close(),
`不处理异常`
只是 在 发生异常时, 结束程序 或 吞下异常
class DBConnection // 负责 数据库连接 的 class
{
public:
static DBConnection create(); // return DBConnection object
void close();
};
class DBConn // 管理 DBConnection object
{
public:
void close() // 供 DBConn 的 client 去 间接调 DBConnection close()
{
db.close();
closed = true;
}
~DBConn()
{
if( !closed )
{
try
{
db.close();
}
catch(...)
{
log close 调用失败;
... // 结束程序 或 吞下异常
}
}
}
private:
DBConnection db;
bool closed;
};
5 ctor/dtor
期间 Never 调 vf
`1 derived class object 的 base part 构造/析构 期间, object 的 type 是 base class 而非 derived class`
#include <iostream>
#include <typeinfo>
class B // 交易
{
public:
B() { vf(); }
virtual void vf() const = 0;
};
void B::vf() const
{
std::cout << typeid(*this).name() << "\n"; // class B
}
class D : public B
{
public:
virtual void vf() const { }
};
int main()
{
D d;
}
// ctor / dtor 调用链 中 不要含 vf`
class B
{
public:
B() { init(); } // 调 non-virtual func
virtual void vf() const = 0;
private:
void init()
{
vf(); // 调 vf
}
};
`2 base class func 想 处理 Derived class 的 static mem data, 怎么办?`
|
|/
LogInfo: 不含 未初始化 non-static mem data
`无法 用 vf 从 base class 向下 调用`
引用方式 调 Base ctor`
/ |\
`Derived ctor` | 作 实参
\ |
调 自身 private static mem func` 返回 LogInfo
class B
{
public:
explicit B(const std::string& logInfo)
{
vf(logInfo);
}
void vf(const std::string& logInfo) const; // non-virtual
};
class D : public B
{
public:
D()
: B( createLog( params ) ) // logInfo 传给 base class ctor
{ ... }
private:
static std::string createLog( params );
};
6 令 operator=
返回 reference to *this
1 为实现
`连续赋值, operator= 必须 return a ref to lhs 实参: operator= 为 class mem 时, lhs 实参 为 *this`
```
A&
operator=(const A& rhs)
{
...
return *this; // return reference to current object (*this)
}
2 该协议 也适用于 其他 `赋值 运算` / `参数 非 const A&`
A& operator+=(const A& rhs) // 适用于: += 等
{
...
return *this;
}
A&
operator=(int rhs) // 适用于: 参数 非 const A&
{
...
return *this;
}
3 这只是个 协议, 并无强制性
只是 标准库中
string / vector / complex / shared_ptr 都遵守
7 在 operator=
中 处理 自我赋值
1 `自我赋值:` 发生在 `对象被赋值给自己` 时
愚蠢 但 合法
(1) `显而易见 的 自我赋值`
class A { /// };
A a;
...
a = a; // 赋值给自己, 没人会写出这样的 code
`(2) 别名 ( aliasing )` 引起的 `隐晦的 自我赋值`
`别名: 用 多个方法 指涉 同一对象`
a[i] = a[j]; // 潜在的自我赋值: i = j 时
*px = *py; // 潜在的自我赋值: px 和 py 指向 同一 obj 时
`1) 操作 ptr 或 ref:` 它们 `指向 多个 同类型 对象`
要考虑 对象是否为 同一个
`2) 两个 对象 来自 同一继承体系, 类型不同` 也可能 造成 `别名`
pointer / reference to base class 可 指向 derived object
class Base { ... };
class Derived: public Base { ... };
// rb 和 *pd 可能是 同一对象
void dosth(const Base& rb,
Derived* pd);
`2 管理资源 时, 要保证 self-assignment-safe:` avoid `在停止使用 资源前, 意外释放它`
`自行 管理资源 / 用对象 管理资源` 时, 都要保证 `self-assignment-safe`
——————————————————————————————————————————————————————————————————
1) 自行 管理资源 | `client code 要保证` ...
——————————————————————————————————————————————————————————————————
2) 用对象管理资源 | 对象 相应 `class 的 operator= 要保证` ...
——————————————————————————————————————————————————————————————————
例: 用 class 保存 pointer to a `动态/heap 分配` 的 bitmap
class Bitmap { ... };
class A
{
private:
Bitmap* pb; // ptr: 指向 heap 分配的 bitmap
};
`1) operator= delete lhs.ptr -> new use rhs's ptr 所指 obj 的 copy -> return *this`
若 this == &rhs
-> rhs's internal ptr/memory 先被 delete
`return 的 *this 的 ptr指向 已 deleted 对象`
A&
A::operator=(const A& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
|
| `保证 self-assignment-safe` 的 3 种 方法
|/
`(1) [1] 等价 test -> [2] delete (原) internal ptr -> [3] (新) internal ptr 指向 copy of rhs's pointed content`
A&
A::operator=(const A& rhs)
{
if(this == &rhs) // identity test
return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
`(2) [1] record (原) internal ptr -> [2] (新) internal ptr 指向 copy of rhs's pointed content -> [3] delete (原) internal ptr`
=> 保证 `exception-safe` => 往往 `自动获得 self-assignment-safe`
`1) copy rhs's ptr 所指 content 前, 别 删除 rhs's ptr`
`2) new Bitmap 抛出异常 => delete pOrig 不执行 => lhs 的 原 internal ptr (pb) 及 lhs 保持原状`
3) 可不用 identity test:
加上 identity test 效率 可能 `更高 ( self- 发生频率高 ) / 低 ( test 要成本 )`
A&
A::operator=(const A& rhs)
{
Bitmap* pOrig = pb; // [1] record `原 internal ptr`
pb = new Bitmap(*rhs.pb); // [2] `新 internal ptr` 指向 rhs's *pb 的 copy
delete pOrig; // [3] delete 原 internal ptr
return *this;
}
`(3) copy and swap`
class A
{
void swap(A& rhs); // 交换 *this 和 rhs's data
};
A& A::operator=(const A& rhs) // pass by reference: rhs 是 = 右侧对象
{
A tmp(rhs); // make a copy of rhs's data
swap(tmp); // 交换 *this 和 copy 的 data
return *this;
}
8 Copy 对象时 勿忘其 每个 part
// B
class B
{
public:
B(const B& rhs);
B& operator=(const B& rhs);
private:
std::string name;
};
B::B(const B& rhs)
: name(rhs.name){ }
B& B::operator=(const B& rhs)
{
name = rhs.name;
return *this;
}
class B
{
public:
...
private:
std::string name;
Date da; // class Date { ... };
};
// copying 函数 要修改: 这里不再赘述
// 继承: copying 函数 应 调 Base 适当 函数
class D: public B
{
public:
D(const D& rhs);
D& operator=(const D& rhs);
private:
int derivedPart;
};
D::D(const D& rhs)
: derivedPart(rhs.derivedPart){}
D& D::operator=(const D& rhs)
{
derivedPart = rhs.derivedPart;
return *this;
}
// 继承: copying 函数 调 base class 适当 函数
D::D(const D& rhs)
: B(rhs),
derivedPart(rhs.derivedPart) {}
D& D::operator=(const D& rhs)
{
B::operator=(rhs);
derivedPart = rhs.derivedPart;
return *this;
}
=> Derived 的 copying 函数 应调 Base 适当 函数`
copy 每个 part
1) copy local mem data
2) 调 base class 内 适当 copying 函数
`2 copy ctor / assignment 相同 code` 段 `去重`
网友评论