vptr和vtbl(虚指针和虚函数表)
c++代码的抽象类是 -> 类当中只包含纯虚函数
当一个类有虚函数,即便类当中没有成员变量.他的对象大小也会有一根指针大小 -> 由操作系统决定指针多大
虚函数
子类的对象里面有父类的成分
示例结构代码:
分析:
-
继承结构是
C类继承B类.B类继承A类 -> 继承包括数据继承和函数继承 -> 继承的函数是继承函数的调用权.所以父类有虚函数子类一定有 -
内存层面结构图:
-

-
当一个类里面有虚函数的时候(无论多少个) -> 类当中就会携带一根指针的内存空间
-
实框框起来的就是父类的一部分
-
声明虚函数以后类的内存地址当中存在一根指针,该指针指向类对于的虚表,虚表里面存放的都是虚函数的指针
-
-
如果在上诉条件下
new c会得到一根指向c的指针,如果通过指针p调用vfunc1()-
c的时候会call地址.跳到地址的位置然后在返回回来 -> 静态绑定 -
通过指针调用虚函数就是动态绑定 -> 面向对象的关键点 -> 通过通过
new的指针.找到vptr虚指针.在找到vtbl,从虚表当中查找指向的函数 -
上诉逻辑翻译成
c代码是-
(* p->vptr[n]) (p); // 指针p找到虚指针 p->vptr,然后根据索引[n]找到指向函数的指针(p) n是索引值.由编译器决定
-
-
-
为了让容器可以存放内存大小不同的元素,那么容器里面必须放置指向父类的指针 ->
list<A*> myLst;-
为什么是指向父类? -> 因为在设计的时候可能设计一个抽象的父类.在子类实例化的时候才会去告诉父类自己是什么子类. -> 子类当中有一个
drwa()函数
-
-
c++当中有虚函数,那么虚函数指向虚表当中的什么类型那么就会去调用什么类型的draw(draw写成virtual function这就是好的 -
他的设计原因是因为
c当中如果要实现这个逻辑那么就需要判断指针指向什么类型.如果将来新增新的子类那么就需要增加判断代码
总结:
-
c++编译器看到函数首先要考虑把函数静态绑定还是动态绑定-
静态绑定 ->
call 地址->call是汇编语言的一个动作 -
动态绑定条件:
-
必须通过指针调用
-
指针必须是向上转型(
upcat) -> 什么意思?-
例如上述代码:
new c得到的是一个c的对象.但是c继承于B,B继承于A那么在一开始声明的时候就是A类型. -> 又因为继承关系会有父类的一部分.所以可以实现向上转型
-
-
调用的是虚函数
-
-
满足这三个条件就会实现动态绑定 -> 虚机制
-
-
上诉的用法就是多态
-
一个声明
-
实际指向不同的东西
-
不过这些东西都必须是子类
-
上述就是多态的内存层面的实现
C当中的实现
继承在C当中的实现
在c代码当中.由于并没有直接的继承关键字(编译器层面没做这个设计),所以在c当中的继承类似c++中的复合的概念
示例代码:
c++中继承、多态的特性在c中的实现 -> c并没有直接提供这些概念.所以在实现的时候内存层面的处理并没有c++做得那么的好,更多的是最简单的模型
示例代码:
由于c当中无法在结构体内声明具体方法,所以在实现起来的时候实现的方式是使用指针指向实现的模拟c++当中的虚函数

浙公网安备 33010602011771号