侯捷手把手教学C++(下)笔记

  • class中的类型转换函数:将这种class类型转为其他类型
class A {
   operator double() const { ... } //将A类型转换成double类型
   ...
} 
1. 这里转为的类型不一定是非double,只要是上文出现过的type即可,可以是内置类型也可以是自定义的;
2. 无需参数,因为只是转换类型,自然不需要什么参数;
3. 无需返回类型,因为返回类型就是转成的那个类型,因此省略不写;
4. 只是转换class对象的类型,并不会修改class对象的数据成员,往往加const;
  • non-explicit-one-argument ctor:将其它类型转换为这种class类型
class A {
public:
		A(int x1, int x2 = 2) : a(x1), b(x2) { }
  	A operator+(A& A1) {	return A(a+A1.a, b+A1.b); }
 		...
private:
  	int a;
  	int b;
}
A A1(1, 1);
A A2 = A1 + 4;//调用non-explicit构造函数,将4转换为A(4, 2),然后调用operator+

non-explicit 且 只需一个实参就够了的构造函数:当需要一个这种class类型的时候,会隐式调用此构造函数将那个实参创建出一个class对象
这里只需一个实参就够了,意思是构造函数的所有参数都有默认实参,或除第一个以外后面的所有参数都有默认实参,或者干脆就只有一个参数,总之,必须是non-explicit,然后可以通过一个实参就能调用构造函数创建一个类对象;
  • pointer-like class:智能指针和迭代器
需要像普通指针一样支持*, ->操作;
智能指针class中,肯定包含有一个普通的指针;
注:重载->时,智能指针sp作用在->上得到包含的那个普通指针px,按理说->已经被消耗掉了,但为了解释方便,px依然作用在->上;

在这里插入图片描述

迭代器主要用于遍历容器,因此不同的迭代器还需要重载一些额外的操作符;

在这里插入图片描述

  • function-like class:
重载了()的class,这种class实例化出的对象就叫函数对象(或仿函数);
这种class需要根据参数个数继承unary_functio或binary_function;

在这里插入图片描述
在这里插入图片描述

  • 自己将一系列class,变量都放在自己的一个namespace中,防止协同完成项目时出现命名冲突;

  • 类/函数/成员模板

类模板:使用时需要指定T
函数模板:使用时无需指定,编译器会根据传入的参数类型自动推导出T
//有问题???????????????  
成员模板:为了使A的构造函数更有弹性,可以对A的构造函数写成模板,以便支持传入A的派生类的指针用于A的初始化
  • 模板特化/偏特化
模板特化:与模板泛化相反,泛化指在使用时根据指定的类型生成对应版本的代码,而特化已经提前确定好了类型,不能再指定了
  
下面例中上面的是泛化版本,下面又额外写了特化版本(当然下图中在使用时,当泛化和特化都可以匹配时,优先特化)

在这里插入图片描述

模板偏特化:个数的偏/范围的偏;即从某处缩小泛化支持的范围;

个数偏特化:原本泛化版本需要指定n个类型,再额外写只需指定前面若干个类型的版本,后面使用默认值
范围偏特化:原本泛化版本主要指定T类型,再额外写一份需要指定某种T类型的版本,例如支持T*,T&等等

在这里插入图片描述
在这里插入图片描述

  • 模板模板参数
模板需要指定的某个类型自己也是一个模板;
下图的例子中还有问题??????????,容器模板本身接受不止一个类型,而shared_ptr和auto_ptr只需指定一个类型;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • variadic template
之前的模板的类型个数是固定的,现在可以是变化的,在使用时根据输入的个数;

在这里插入图片描述

  • vptr和vtbl
继承:不仅继承数据也继承函数(这里继承的是函数的调用权,而不是具体函数的内存大小, 因为一个子类对象也是一个基类对象,因此(如果权限允许的话)自然可以用一个子类对象去调用基类的成员函数); 
  
静态绑定:实际会被编译为call 某个地址;   //跳到那个地址处执行那段函数代码
动态绑定:满足通过指针调用 + 这个指针是向上转型(up cast) + 调用的是虚函数,编辑器就会把这种调用动作编译为虚函数调用,到底调用的是那一块代码是看这个指针指向的是谁,即下图中的p指针

疑问:???????????????????????
下图中左下角那个c(C object)中从B继承而来的部分,其中m_data1和m_data2实际都是B从A继承而来,为何只写Class A::m_data1

下图中,B,C对象是一个A对象;
数组myList中只能存储A*,而不能是具体的A,B,C...因为不同类型大小是不同的;
因为基类A可能有各种各样的子类,这些子类对象都是一个A,因此可以用A*去指向它们;
等到调用时,根据这些指针到底指向的是哪种类型(如同下图的p指向一个C对象c), 就通过vptr和vtbl去调用它真正的版本,这样就实现了多态;

在这里插入图片描述

下面是个静态绑定和动态绑定的实例:

在这里插入图片描述

  • 与new,delete有关的重载
我们平时使用new的动作,例如:
A* a = new A; 实际被编译器分解为3个动作(operator new + static_cast + ctor)
delete a; 被分解为2个动作(dtor + operator delete)
B* b = new B[3]; 实际被编译器分解为3个动作(operator new[] + static_cast + 3次ctor)
delete[] b; 被分解为2个动作(3次dtor + operator delete[]) 

这个分解动作是由编译器自动完成的,但我们可以重载operator new,operator new[], operator delete, operator delete[]
来实现一些内存管理;
  1. 重载 ::operator new,::operator delete,::operator new[ ],::operator delete[ ]
重载global的这些函数

在这里插入图片描述
2. 重载member operator new,member operator delete

为某个class重载operator new和operator delete,当需要new或delete这种class时,编译器就会调用我们重载的版本

在这里插入图片描述

为某个class重载operator new[], operator delete[],当需要new或delete这种class的数组时,编译器就会调用我们的重载版本

在这里插入图片描述

注:我们可以跳过重载版本,强制去使用global版本的这些函数,而不是为class重载的版本
Foo* pf = ::new Foo;
::delete pf;

在这里插入图片描述

下图为gnc4.9下的情况,没有像VC6.0那样分配上下cookie,以及为了字节对齐多分配的内存;//暂时这么理解
一个Foo对象为12bite,若Foo含有虚函数,则多4bite存vptr;
若分配数组时,多分配4bite用于存元素个数;
堆上分配内存时,地址时从低到高分配的,若分配数组时,最低地址处填的是元素个数,后面依次才是数组中的若干个元素;
由于我在class Foo中对这些operator函数重载,因此会调用我的重载版本;若我调用时在new和delete前面加上::,就会调用global版本;

在这里插入图片描述
在这里插入图片描述
3. 重载new(),delete() ; 即placement new / delete

可以重载多个版本的class member operator new(),但第一参数必须是size_t
也可以重载多个版本的class member operator delete(),但只有构造函数发出异常时,才会调用这里重载的operator delete();

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值