1. 虚函数机制
之 5大问题
(1) 虚成员函数指针 vs. 虚成员函数地址
vtbl 中
的 偏移/数组索引 vs. 所存储的各 虚成员函数 真正地址
#include <iostream>
using uintPtr = unsigned int*; // 32位 指针类型, 表示 虚成员函数地址
class Base
{
public:
Base()
{
std::cout << "虚成员函数指针: &Base::vf1 = " << &Base::vf1 << std::endl;
Base::showVtbl(this, "Base");
std::cout << "==================\n\n";
}
// vtbl: 虚成员函数地址 数组, elem 类型转换为 uintPtr => vtbl 可表示为 uintPtr*
// vptr: 指向 vtbl => vptr 可表示为 uintPtr**
static void showVtbl(Base* pb, const char* classType)
{
uintPtr** vptr = reinterpret_cast<uintPtr**>(pb);
uintPtr* vtbl = *vptr;
std::cout << classType << " vtbl: " << vtbl << std::endl;
// 硬编码, 只是 demo
std::cout << "虚成员函数指针 与 虚成员函数地址 的 逻辑关系, 语法不能这么写" << std::endl;
std::cout << "虚成员函数(真正)地址: pb->vptr[n = &" << classType << "::vf1] = " << *vtbl << std::endl;
}
virtual void vf1() { std::cout << "Base::vf1" << std::endl; }
};
class Derived : public Base
{
public:
Derived()
{
std::cout << "虚成员函数指针: &Derived::vf1 = " << &Derived::vf1 << std::endl;
Base::showVtbl(this, "Derived");
}
virtual void vf1() { std::cout << "Derived::vf1" << std::endl; }
};
int main()
{
Derived d{};
std::cout << "vf 调用: (*pb->vptr[n])(pb)" << std::endl;
return 0;
}

(2) Ctor 为虚会怎样 ?
对比 Ctor 与 普通函数 func 的 汇编指令
`compiler explorer`
[1] 非继承
Ctor + func 均 non-virtual
两者无区别
[2] 继承
B 的 Ctor 比 A 的 Ctor 多了 3 条指令
前2条: 输入 隐含参数 this
第3条: 调 A 的 Ctor
[3] func 设为 virtual
A B 的 Ctor 都会多出 3 条指令
从 字面意思可猜出,
这是记录 vtbl 的 地址
`A/B 的 Ctor 只会记录 A/B 的 Vtbl 的地址 (vptr)`
1] 保证 `A 和 B 的 object 在 调用 vf 时 能区分 你我`
|
| 即 所谓的
|/
动态绑定`
|
| 实际 所有的
|/
vf 绑定 早在 Ctor 中 就安排好了
2] Ctor 执行过程中, 只能到 `Ctor 所属类` 的 vtbl 中 找 vf
1] 而不是 Ctor 的 指针相应的 对象 所属类 的 vtbl 中 找 vf
若 Ctor 本身为 vf / Ctor 中调用 vf
=> 均 无法实现 多态
2] vf 调用 实现多态: 必须用 对象的 ptr 或 ref 调用
Ctor 执行中 对象还没产生
3] vf 实现多态
ptr -> 运行时 对象 -> 对象的确切类型 的 vtbl 的 vptr -> vtbl + 虚成员函数指针 -> vf 真正地址 -> 调用
pb derived
若 ctor 为虚, 据 Ctor() 只能找到 Ctor 所属类对象, 无 运行时对象 这一说
总结
Ctor 与 普通函数 一样
都需要 夹带1个 隐含参数 this 指针
Derived 的 Ctor 会 夹带调用 Base 的 Ctor
若存在 vf, Ctor 会记录 vtbl 的地址 vptr -> 保存在 object 里




2 智能指针
3 swap
右值引用
的 创建 + 应用
为 swap
定义 类 的 移动操作
(1) 右值引用 的 创建 + 应用 (C++程序设计语言 7.6 节)
1) 2 种创建方法
1> static_cast<T&&>
2> std::move(x)
move(x)
`并不真的 移动 x`,
`只是 为 x 创建 右值引用`
|
| note:
| `没 move 函数 时, std::move 默认 copy 操作`
| => move(内置类型) = 本身
|/
之后 `用 右值引用 初始化/赋值 左值时, 才 移动`
2) 应用
1> 所有 `标准库容器` 都 提供了 `移动` ctor/赋值运算符
1] 用于 `插入新元素`
insert()
push_back()
都提供了 接受 `右值引用` 的 版本
2] 清空 容器
void f(vector<int>& vec)
{
vec.clear(); // 清空 v
swap(vec, vector<int>{} ); // 清空 v
v = {}; // 清空 v
}
2> 实参转发
23.5.2.1 / 28.6.3
3> 交换
template<typename T>
void swap(T& a, T& b)
{
T tmp {a}; // 创建对象: 初始化
a = b; // copy
b = tmp; // copy
}
只想 在 a b tmp 间 移动数据而已, 不想 copy
|
| static_cast<X&&>(x), x 类型为 X
| 结果是 X&& 类型 的 右值引用
|
| note: `为 swap` 定义 `类 的 移动操作`
|/
template<typename T>
void swap(T& a, T& b)
{
T tmp {static_cast<T&&>(a) }; // 创建对象: 初始化 + 显式 `移动` 左值
a = static_cast<T&&>(b); // 移动
b = static_cast<T&&>(tmp); // 移动
}
| static_cast 繁琐
|
| move(x) <=> static_cast<X&&>(x), x 类型为 X
|/
template<typename T>
void swap(T& a, T& b)
{
T tmp {move(a) };// 从 a 中 `移出` 值
a = move(b);
b = move(tmp);
}
|
| 问题: 只能交换 左值
|
| 解决: 加 2个重载版本, 以 交换 `左值 与 右值`
|/
template<typename T> void swap(T&& a, T& b);
template<typename T> void swap(T& a, T&& b);
4. 函数指针 / 函数对象 / lambda
1) 函数指针 ( 值 类型 => `不能用 typename 限定` ) 作 模板参数
template <typename Key, typename V, bool (*cmp)(const Key&, const Key&) >
class map1;
2) 函数对象 -> 是否能 (隐式)转换 为 -> 函数指针
一般不能, 除非 `带 转换为函数指针` 的 `类型转换运算符` ?
3) lambda -> 是否能 转换 为 -> 函数指针
匿名(普通) lamnda: 能转换
命名 lambda : 与 函数对象 规则一样
4) lambda -> 不能 转换 为 -> 函数对象类型
=> 想 用 lambda 相应的 函数对象类型
命名 lambda ( auto ) + decltype()
#include <iostream>
#include <string>
// =======1 函数指针 ( 值 类型 => 不必用 typename 限定 ) 作 模板参数 + 函数指针 作 模板实参
// 版本1: 函数指针 作 模板参数 + 函数指针 作 模板实参
template <typename Key, typename V, bool (*cmp)(const Key&, const Key&) >
class map1
{
public:
map1() { /* ... */ }
// ...
};
bool insensitive(const std::string& s1, const std::string& s2) { /* ... */ return true; }
void test1()
{
map1<std::string, int, insensitive> m;
}
// 版本2: 函数指针 别名 作 模板参数 + 函数指针 作 模板实参 <=> 版本1
template<typename T>
using CmpPtr = bool (*)(const T&, const T&);
template <typename Key, typename V, CmpPtr<Key> >
class map2
{
public:
map2() { /* ... */ }
map2(CmpPtr<Key> c) : cmpPtr{ c } { /* ... */ }
// ...
private:
CmpPtr<Key> cmpPtr;
};
void test2()
{
map2<std::string, int, insensitive> m2;
// Note: 编译报错: 第3模板参数 只能是 值(类型), 不能是 类型(类型)
//map2<std::string, int, CmpPtr<std::string> > m21(insensitive);
}
// =======2 函数对象 ( 类型 类型 ) 作 模板参数
template <typename Key, typename V, typename Compare = std::less<Key> >
class map3
{
public:
map3() { /* ... */ }
map3(Compare c) : cmp{ c } { /* ... */ } // 覆盖 默认 Ctor
// ...
private:
Compare cmp {}; // 默认比较
};
// 函数对象
class Cmp
{
public:
bool operator()(const std::string& a, const std::string& b) { return a < b; }
};
// 函数对象: 带 转换为函数指针的 `类型转换运算符`
class Cmp2
{
public:
bool operator()(const std::string& a, const std::string& b) { return a < b; }
operator CmpPtr<std::string>() { return insensitive; }
};
void test3()
{
map3<std::string, int, std::greater<std::string> > m3;
// 版本3: 函数对象 作 模板参数 + 函数指针 作 模板实参
map3<std::string, int, CmpPtr<std::string> > m31{ insensitive };
// 版本4: 函数对象 作 模板参数 + 能转换为 函数指针 的 lambda (匿名 lambda) 作 模板实参
map3<std::string, int, CmpPtr<std::string> > m32{ [](const std::string& a, const std::string& b) { return a < b; } };
// 版本5: 函数对象 作 模板参数 + 能转换为 函数指针 的 函数对象 (必须提供 转换为函数指针的 类型转换运算符) 作 模板实参
Cmp2 cmp2;
map3<std::string, int, CmpPtr<std::string> > m33{ cmp2 };
// Cmp cmp;
// Note: 编译报错: 无法从 函数对象 cmp 转换为 CmpPtr<std::string> 类型
// map3<std::string, int, CmpPtr<std::string> > m34( cmp );
}
void test4()
{
// Note: 编译报错: lambda -> 不能 转换 为 -> 函数对象类型
// 想 用 lambda 相应的 函数对象类型 -> solu: 命名 lambda ( auto ) + decltype()
// map3<std::string, int, Cmp > m35{ [](const std::string& a, const std::string& b) { return a < b; } };
auto cmp = [](const std::string& a, const std::string& b) { return a < b; };
map3<std::string, int, decltype(cmp) > m36{ cmp };
}
int main()
{
test1();
test2();
test3();
return 0;
}
5 数据抽象 和 封装
(1) 数据抽象
interface 与 implement 分离
(2) 封装
1) hide implement detail
2) 2种形式
class / function
(3) vector vs. 数组
1) vector
抽象
使用 时, 只需 考虑其 interface
封装
是 class template,
2) 数组
非 抽象 / 非 封装
6 this 是 const ptr: 编译器 隐含定义 的 non-static memFunc 的 Para
1 必须 用 this 的情形
需 `整体 use` 调用 non-static memFunc 的 `obj 时
A& A::f()
{
...
return *this;
}
2 this 是 const ptr
A * const this
this 本身 不可改变
const mem fuc: 保证不 modify *this ( 的 immutable mem data )
=> 只能 return *this 的 const reference: const A&
A& A::f()
{
...
return *this;
}
const A& A::f() const
{
...
return *this;
}
7 const memFunc
1 memFunc1 被 memFunc2
`non-const 版本 和 const 版本 都调用` 时
memFunc1 `必须为 const 版本`
2 基于 const 的 2种 重载
[1] 基于 ptr para 是否 指向 const type, 可重载 func
[2] 基于 mem func 是否 为 const, 可重载 mem func
memFunc 的 const / non-const 版本 是 `重载函数`
const memFunc 中
|
| this 的 type 为 const ptr to const A(classType)
|/
const A* const this
1] this 本身
2] this 所指 obj( 的 immutable mem data)
均 不可改变
3 用于 public function 的 `practiacl private function`**
小 private function 的 意义: 连续调用
支持 对其 caller function 的 调用
return 的 non-const obj 上
接着调用 该 class 的 另一 mem func
a1.display(cout).set(1);
4 const & mutable ( keyword : 可变 )
const mem func 可 modify obj 的 mutable mem data
mutable mem data 不能为 const`
8 类 scope
必须 explicitly 用 class name + scope operator:: 限定的场景
return type
在 mem func name 之前 => 不在 class scope 中
9 ctor
1 ctor 不能为 const
const / non-const obj 用同一 ctor 创建
2 分 2 阶段
[1] 初始化
分配 存储空间 + 编译器 中 构造 symbol table, 以 关联 标识符 与 memory
[2] 赋值
擦除 旧值 + 写 新值
`无论是否 explicitly use 初始化列表, 初始化阶段 都会执行`
3 `必须 用 初始化列表` 进行初始化 的 `2种情形`
[2] ref type 成员
[3] const 成员
ctor func body` 中 对这2种 mem 的 `赋值 不起作用`
4 单 para ctor: para type 到 class type 的 conversion
`explicit 位置: 只在 类内 用`, 类外 不用
10 友元 friend
1
友元类
友元函数
memFunc
non-memFunc
2 friend declaration / scope`
`class / non-mem func 设为 friend 时, 相当于 global declaration
=> 不必 再 forward declaration`
3 keyword friend 位置
1) 只能放 本 class A 内`
2) friend `不是 本 class 成员`
=> friend 放在 本 class public / protected / private 区都一样
网友评论