【侯捷】C++程序设计II--兼谈对象模型

【侯捷】C++程序设计II--兼谈对象模型

  • 0. 课程目标
  • 1. 转换函数
    • 1.1 operator type() const
    • 1.2 explicit
  • 2. pointer/function-like class
    • 2.1 智能指针
    • 2.2 迭代器
    • 2.3 function-like class
  • 3. template specialization
    • 3.1 类模板
    • 3.2 函数模板
    • 3.3 成员模板
    • 3.4 模板特化
    • 3.5 模板偏特化
    • 3.6 模板模板参数
  • 4. 标准库
    • 4.1 variadic template
    • 4.2 auto
    • 4.3 range-base for loop
    • 4.4 reference
  • 5. Object Model
    • 5.1 vptr与vtbl
    • 5.2 this
    • 5.3 动态绑定
  • 6. 返场补充
    • 6.1 const
    • 6.2 动态分配
      • 6.2.1 new/delete表达式
      • 6.2.2 重载new/delete
      • 6.2.3 重载placement new
      • 6.2.4 basic_string使用new(extra)

本系列相关链接
【侯捷】C++11

0. 课程目标

包含泛型编程、对象模型,共有25小节;

学习视频观看顺序如下,第一行为前序号,第二行为后序号:
14 17 22 24 18 21 16 19 20 26 35 34 28 36 25 27 33 32 30 29 31 23 37 38 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 19(1) 20 21 22 23 24

关注内容如下,其中c++11的部分见另一个博客;

其中泛型编程即模板技术;
对象模型包括this指针、虚指针、虚表、虚机制、虚函数及多态;
【侯捷】C++程序设计II--兼谈对象模型_第1张图片

1. 转换函数

1.1 operator type() const

class转换出去:
数据类型转换操作符的重载,没有返回值类型,通常末尾加const;

转换进class来:?
构造函数;

1.2 explicit

non-explicit-one-argument ctor,只有一个无缺省值的构造函数,如果不加explicit ,则可能发生自动类型转换继而调用构造函数;
如果不想允许,则需要加explicit;

2. pointer/function-like class

例如,智能指针、迭代器;
也就是必须由*操作符、->操作符;

2.1 智能指针

template<class T>
class shared_ptr{
public:
  T& operator*() const { return *px; }
  T* operator->() const { return px; }
  shared_ptr(T* p) : px(p) { *pn = 0; }
private:
  T* px;
  long* pn;
};

另外,操作符->有特殊功能,也就是会继续指下去;

2.2 迭代器

除了*操作符、->操作符,还需要设计++、–等若干操作符;

【侯捷】C++程序设计II--兼谈对象模型_第2张图片
这里荷包蛋表示迭代器;

T& operator*() const { return (*node).data; }
T* operator->() const { return &(operator*()); }

2.3 function-like class

也即仿函数
也就是能接受小括号()操作符的class,可以带形参;

标准库中的仿函数,都会使用奇特的base classes(没有数据,size=1虽然理论上是0),例如unary_function、binary_function;

template<class Arg, class Result>
struct unary_function {
	typedef Arg argument_type;
	typedef Result result_type;
};

template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

Namespace
防止名称冲突;简单,不说了;

3. template specialization

3.1 类模板

与普通类可以相互继承,共有4种组合方式;

3.2 函数模板

编译器会对function template进行实参推导,其中&、const的推导类似auto,比较复杂;

3.3 成员模板

即类模板中的函数模板;
把一个由Derived1和Derived2构成的pair,放进一个由Base1和Base2构成的pair中,可以;
反之,不可以;
为了实现这个,就要在构造函数中用到成员模板;
【侯捷】C++程序设计II--兼谈对象模型_第3张图片
再例如,智能指针为了实现up-cast,也需要用到成员模板技术;
【侯捷】C++程序设计II--兼谈对象模型_第4张图片

3.4 模板特化

有特化版本时,调用时优先调用特化(specialization)版本;

3.5 模板偏特化

偏特化,partial specialization;

  1. 个数的偏
    例如,原来有3个泛化的占位,现在指定其中1个数据类型;绑定的顺序要连续;
  2. 范围的偏
    例如,从任意T,特化为指针类型T*;

3.6 模板模板参数

template template parameter,也就是模板参数本身又是一份模板;
非常常用,例如标准库:

template<typename T, template<typename T> class Container>
class XCls{
private:
  Container<T> c;...
};

template<typename T>
using Lst = list<T, allocator<T>>; //容器需要接收2个模板参数

///
XCls<string, Lst> mylst1;
// XCls mylst2; //error

4. 标准库

详见博客【侯捷】C++11;
【侯捷】C++程序设计II--兼谈对象模型_第5张图片

4.1 variadic template

语法糖,数量不定的模板参数;也就是将一包拆分成一个+剩下一包;
注意**"…"**的三个位置,没有道理可言,是规定;
**sizeof…(args)**可以获悉一包到底是几个;

void print(){}

template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
    cout << firstArg << endl;
    print(args...);
}

4.2 auto

自动类型推导;

list<string> c;
auto ite = find(c.begin(), c.end(), target);
//
// auto ite; 这两行出错,这样编译器无法知道ite到底推导成什么类型
// ite = find(c.begin(), c.end(), target);

4.3 range-base for loop

基于范围的for遍历;

vector<double> vec;
for(auto elem : vec) { cout << elem << endl; }  // pass by value
for(auto& elem : vec) { elem *= 3; }            // pass by reference

4.4 reference

对象和其引用的大小相同,地址也相同,不过这些都是编译器可以制造的假象;
引用一旦被定义,之后再也不能指向其他人;
引用可以传递;

引用的常见用途:传参、传返回值;

注意:形参的引用不属于函数签名,例如

double imag(const double& im) {...}
double imag(const double  im) {...} // 不可并存,否则调用的时候编译器不知道你想调用谁

那么,函数末尾的const属于函数签名吗?属于,是!!

5. Object Model

5.1 vptr与vtbl

虚指针与虚表;正是它们的配合,才实现了强大的“多态”。
多态的必要条件:

  • 通过指针调用
  • 这个指针是up-cast
  • 调用虚函数

所谓“继承函数”,继承是函数的调用权,而不是函数的内存;

带虚函数的class包含一个虚指针,虚指针指向一个虚表,虚表中有若干指针,分别指向各自的虚函数。
【侯捷】C++程序设计II--兼谈对象模型_第6张图片

5.2 this

通过一个对象调用函数,对象的地址就是this指针;
涉及的设计模式叫“Template Method";
【侯捷】C++程序设计II--兼谈对象模型_第7张图片

5.3 动态绑定

B b;
A a = (A)b;
a.vfunc1(); //静态绑定,调用A::vfunc1;

A* pa = new B;
pa->vfunc1(); //满足三要素,动态绑定,调用B::vfunc1;

pa = &b;
pa->vfunc1(); //同上,满足三要素,动态绑定,调用B::vfunc1;

6. 返场补充

6.1 const

const object只能调用const成员函数;
non-const object既能调用const成员函数,也能调用non-const成员函数;
当成员函数的const和non-const版本(overloading,重载)同时存在,const object只会调用const版本,non-const object只会调用non-const版本;

例如,标准库中string的[]操作符,如果多个string共享一个内存,当使用[]时用户可能是访问、也可能是修改。如果想修改的内容是共享的,必须用"Copy-On-Write"即"COW",也就是单独拷贝一份再修改,而不影响其他string。
此时,需要两个版本的[]函数,一个是const函数,不必考虑COW;另一个是non-const函数,需要考虑COW;这样可以大大提高效率。

6.2 动态分配

6.2.1 new/delete表达式

new表达式实际有3个步骤,其中通过调用operator new() 最终调用mallloc()申请内存;
delete表达式实际有2个步骤,其中通过调用operator delete()最终调用free()释放内存;

new/delete表达式不可改变,而内部调用的operator new/delete()操作符可以重载;用于设计内存池,自己管理内存。

6.2.2 重载new/delete

重载全局new/delete/new[]/delete[]的影响无边无际,危险!
重载class自己的成员操作符,ok。

6.2.3 重载placement new

也就是允许带额外参数的operator new();其中第一个参数必须是size_t;

Foo* pf = new(300, 'c')Foo;

也可以重载class member operator delete(),写出多个版本,分别对应各自的兄弟placement new()。但它们绝不会被delete调用。只有当new所调用的ctor抛出异常,才会调用这些重载版的operator delete()。它只可能这样被调用,主要用来归还未能完全创建成功的object所占用的memory。

6.2.4 basic_string使用new(extra)

【侯捷】C++程序设计II--兼谈对象模型_第8张图片
basic_string需要做引用计数的一个头部,再加实际的string字符串内容,所以需要悄悄摸摸地申请额外的内存。

完结撒花!

你可能感兴趣的:(c++)