文章目录
- C++面向对象编程(下)
- 11. vtpr(virtual pointer)和vtbl(virtual table)
- 1. 前提须知
- 2. 静态绑定和动态绑定
- 3. vtpr和vtbl
- 4. 多态
- 5. 图示一
- 6. 图示二
- 7. template method补充
- 12. const member functions(常量成员函数)
- 1. 形式(const加的位置):
- 2. member functions 与 object 的规则
- 3. const member functions 和 non-const member functions 的特殊规则
- 13. 重载operator new、delete、 new[]、delete[]
- 1. 知识回顾
- 2. 重载 ::operator new、new[] 和 ::operator delete、delete[]
- 3. 重载 member operator new 和 member operator delete
- 4. 重载 member operator new[] 和 member operator delete[]
- 5. operator new、delete、 new[]、delete[] 使用示范
- 1. member operator new、delete、 new[]、delete[]重载写法
- 2. member operator new、delete、 new[]、delete[]调用过程
- 3. 显式调用global operator new、delete、 new[]、delete[]过程
- 14. 重载member operator new(...) 、 delete(...)
- 1. new(...) 、delete(...)和new、delete的区别
- 2. member operator new(...)
- 3. member operator delete(...)
- 4. member operator new(...)、delete(...)重载书写和使用
- 5. 标准库string重载placement new
网站建设公司,为您提供网站建设,网站制作,网页设计及定制网站建设服务,专注于
成都定制网页设计,高端网页制作,对
户外休闲椅等多个行业拥有丰富的网站建设经验的网站建设公司。专业网站设计,网站优化推广哪家好,专业
seo优化排名优化,H5建站,响应式网站。C++面向对象编程(下)
11. vtpr(virtual pointer)和vtbl(virtual table)
1. 前提须知
- 实例对象在内存的表现:
- 函数在内存中的位置,与实例对象占用的内存没有关系;
- 对于一个类的多个对象,函数在内存中也只会有一份;
- 实例对象占用的内存,只会包含对象的数据;如果有虚函数,还会包含一个虚指针;【无论有多少个虚函数,都只会多占用一个指针的空间;】
- 继承:
- 对于父类中的函数,子类只是继承了函数的调用权,不会为子类在内存中另外生成一份函数【因为函数始终只在内存占一份】;
- 如果父类有虚函数,那么子类也一定有虚函数(继承);
- 虽然父类和子类中的非虚函数和数据,命名可以重复(因为二者没有什么关系),但这样的命名风格不好。
2. 静态绑定和动态绑定
- 类型:
- 静态类型:对象在声明时采用的类型,在编译期既已确定;
- 动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的;
- 绑定(Binding):是指将变量和函数名转换为地址的过程。
- 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
- 意味着绑定的函数或者变量,已经在编译阶段,该语句已经被编译成“call 函数地址”或"callq 函数地址"这样的汇编指令格式,并且这些汇编指令中的函数地址在程序编译后是固定不变的,请记住,所有函数都有唯一的地址。
- 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
- 只有虚函数才使用的是动态绑定(实现多态),其他的全部是静态绑定;
- 编译器使用动态绑定的三个条件:
- 通过指针 / 引用调用函数;
- 指针是向上转型后的指针(this指的是子类对象,符合向上转型);【即父类指针指向子类对象】
- 指针调用的是虚函数。
- 简单一句话:
使用基类的引用(指针)调用虚函数
3. vtpr和vtbl
vtpr(虚指针):
- 对于有virtual函数的类,它的实例化对象在内存空间中除了包含数据的大小,还会多出一个指针(4个字节)的大小,同时会放在内存中最开始的位置,这个指针就是vptr(虚指针),它指向了vtbl(虚函数表),即 vptr内容 vtbl的地址。
vtbl(虚函数表):
- vtbl 内容:保存了一个类持有的虚函数的地址;
- 建两个A对象,发现他们的虚函数指针相同,这说明他们的虚函数表属于类,不属于对象。所以虚函数表应该存在共有区;
- 位置:虚函数表放在了全局数据段。
查找过程:
- 在编译时,编译器会为【整个继承关系中的每个虚函数】进行顺序编号,按照这个编号顺序,在vtbl中放置虚函数地址;
- 调用一个实例化对象的虚函数过程:
- 先找到vtptr(在实例化对象所在内存的开头位置);
- 通过vtptr内容,找到vtbl;
- 从vtbl找到对应虚函数的地址,把它当成函数指针去调用;
- 函数指针调用的方式:后面的§,是传递隐藏参数this pointer;
4. 多态
- 多态和动态绑定是一回事(涉及虚函数、虚函数指针、虚函数表)
5. 图示一
- 图中有A、B、C三个对象:
- 对象关系:
- 对象内容:
- A对象:2个虚函数,2个非虚函数,2个int数据;
- B对象:
- 自身的内容:1个非虚函数,1个int;
- 继承的内容:继承了A的2个虚函数,但自己重新定义了其中一个;继承了A的2个非虚函数;继承了A的两个int数据;
- 总内容:2个虚函数(其中一个自定义),3个非虚函数,3个int数据;
- C对象:
- 自身的内容:1个非虚函数,2个int;
- 继承的内容:继承了B的2个虚函数,但自己重新定义了其中一个;继承了B的3个非虚函数;继承了B的3个int数据;
- 总内容::2个虚函数(其中一个自定义),4个非虚函数,5个int数据;
- 三个类的函数个数:
- 4个非虚函数;
- 4个虚函数;
6. 图示二
- A为shape抽象类;
- B、C为具体的形状类;
- A有draw虚函数,B、C自定义了draw函数(相当于上一页的vfunc1());
- 右下角的容器:
- 使用父类指针指向子类的方式(多态),使容器可以包含多种类型;
7. template method补充
- 也可以使用基类指针指向派生类对象 CDocument* cdoc = new CMyDoc(),来调用OnFileOpen()函数;
- 执行Serialize()函数,满足动态绑定三个条件,编译器会把Serialize()写出函数指针调用的形式(图左边);
- 在动态绑定三个条件中的第二个,不太好理解:
- 指针是向上转型后的指针(this指的是子类对象,符合向上转型);
- 【简单理解为只要this指的是子类对象,就满足这个条件。】
- 可能右下角调用OnFileOpen()中的this指针类型是父类指针类型。(这样就有父类指针指向子类对象)
12. const member functions(常量成员函数)
1. 形式(const加的位置):
class Complex{
public:
// const member function(常量成员函数)
double real () const { return resl; }
double imag () const { return im; }
private:
double re, im;
};
2. member functions 与 object 的规则
- const object (要求 data members 不能改动);
- non-const object (data members 可改动);
- const member functions(保证这个成员函数不更改 data members);
- non-const member functions(不保证这个成员函数不更改 data members);
3. const member functions 和 non-const member functions 的特殊规则
13. 重载operator new、delete、 new[]、delete[]
1. 知识回顾
2. 重载 ::operator new、new[] 和 ::operator delete、delete[]
- 一般不重载全局operator new、delete、new[]、delete[];
3. 重载 member operator new 和 member operator delete
- 图中的member operator delete中的optional参数表示该参数可选;
- 该类进行new和delete中,优先找member operator new 和 member operator delete;
4. 重载 member operator new[] 和 member operator delete[]
- 图中的optional指该参数可选;
- 该类进行new[] 和delete[]中,优先找member operator new[] 和 member operator delete[];
5. operator new、delete、 new[]、delete[] 使用示范
1. member operator new、delete、 new[]、delete[]重载写法
2. member operator new、delete、 new[]、delete[]调用过程
不带有虚函数的类:
由上一页可知,sizeof(Foo) = 12【int:4个字节,long:4个字节,string:4个字节(指针)】;
调用 operator new 和 delete:
调用 operator new[] 和 delete[]:
new[] 和 delete[] 调用构造函数和析构函数的顺序相反【看this指针顺序】
带有虚函数的类:
- 唯一不同在于,带有虚函数的类,会多一个虚函数指针的大小;
- sizeof(Foo) = 16;
- 分配5个的大小 == 16*5 + 4 = 84;
3. 显式调用global operator new、delete、 new[]、delete[]过程
- 强迫使用global version,没有member version的打印信息;
14. 重载member operator new(…) 、 delete(…)
1. new(…) 、delete(…)和new、delete的区别
- 括号的意义:operator new() 、delete() 额外设置了参数;
- placement术语介绍:
- member operator new() == placement new;
- member operator delete() == placement delete;
2. member operator new(…)
member operator new() 可以有多个参数,但第一个参数类型一定是size_t(unsigned int),并且这个参数是由编译器传递;
不同版本的 member operator new() 必须有不同的参数列(才能进行函数重载,函数签名不同):
member operator new() 调用方式:
调用时,需要手动传递的参数是第一个参数以后的参数;
下面的 member operator new() 说明,定义时有三个参数;
// 调用 operator new() 和 delete
// new(300, 'c'):说明这个operator new()有三个参数,第一个参数不需要手动传递。
Foo* p = new(300, 'c') Foo(7); // 注意传递new的参数,以及构造参数的位置
delete p;
3. member operator delete(…)
- member operator delete() 也可以重载,也可以不重载;
- 这一步不是必须的,因为不会被delete调用【正常 delete 调用 void operator delete(void*, size_t) 版本】;
- member operator delete() 调用:
- 无论new调用的是哪种版本,只有一般版本的delete会被调用;
- member operator delete() 作用:
- new操作分为三步:
- 只有当 new 中第三步,调用的构造函数抛出异常,才会调用这些 member operator new() 版本对应的 operator delete() ,释放之前 member operator new() 分配的空间;
- 它只可能这样被调用,主要用来归还未能完全创建成功的对象所占用的内存空间。
4. member operator new(…)、delete(…)重载书写和使用
5. 标准库string重载placement new
- 重载 placement new 用于分配额外的空间,作为 string 的内容空间;
- Rep:是标准库 string 用于计数有多少用户共用这个字符串对象的计数器(reference counting 技术);
- reference counting 技术:
- 允许多个拥有共同值的对象共享同一个对象实体,解决了同一个对象存在多分拷贝的问题。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
文章名称:C++面向对象编程(下)-创新互联
网站地址:
http://cdxtjz.cn/article/ejjdc.html