封装,抽象,继承,多态是面向对象编程语言的特点。个人认为抽象和继承都是手段,多态才是目的,继承是多态的基础。
封装:
(1)将属性和函数封装到一个类里边,属性和函数组成一个完整的对象
(2)权限管理,增强安全性
(3)暴露有限的接口,增强了易用性,增强了安全性
抽象:
(1)抽象这个概念存在于很多场景,我们使用函数,本身就是通过函数隐藏具体的实现细节,这也算抽象
(2)c++ 中的抽象,可以使用抽象类来认识,抽象类是包含纯虚函数的类,抽象类不能创建对象
继承:
(1)技术来源于生活,生活中的对象,比如植物,动物,都存在继承
(2)继承带来的好处是子类可以复用父类的属性和函数,复用
(3)c++ 中继承是实现多态的基础
多态:
同一个父类,可以派生出多个子类,每个子类都有自己专有的属性和方法,丰富多彩
1 多态
提到多态我们一般会想到这样一种使用场景:有一个基类,基类里边有虚函数;不同的类继承基类,然后分别在虚函数中实现自己的逻辑,覆盖基类的虚函数;派生类的指针也可以赋值给基类,基类调用虚函数的时候调用的是派生类实现的函数。
上边说的是典型的多态的场景,在有些文章或者书中,也把重载说成是一种多态。本节分别举一个虚函数多态的例子,一个重载多态的例子。
1.1 虚函数
如下代码, Phone 是一个基类,类中有一个虚函数 virtual boid CallUp()。Phone 派生出 3 个类,ApplePhone,MiPhone,OppoPhone。在 main 函数中针对 3 个派生类,分别创建了一个对象。定义了 3 个函数 CallUp1(),CallUp2(),CallUp3(),3 个函数的形参分别是 Phone 引用,Phone 指针,Phone 对象,在 main() 函数中分别调用者 3 个函数。
#include <iostream>
#include <string>
class Phone {
public:
virtual ~Phone() {
std::cout << "~Phone()" << std::endl;
}
virtual void CallUp() {
std::cout << "Phone call up" << std::endl;
}
};
class ApplePhone : public Phone {
public:
void CallUp() {
std::cout << "ApplePhone call up" << std::endl;
}
};
class MiPhone : public Phone {
public:
void CallUp() {
std::cout << "MiPhone call up" << std::endl;
}
};
class OppoPhone : public Phone {
public:
void CallUp() {
std::cout << "OppoPhone call up" << std::endl;
}
};
void CallUp1(Phone &phone) {
std::cout << "CallUp(Phone &phone)" << std::endl;
phone.CallUp();
}
void CallUp2(Phone *phone) {
std::cout << "CallUp2(Phone *phone)" << std::endl;
phone->CallUp();
}
// 派生类可以赋值给基类
// 因为基类中的数据成员,在派生类中都有
// 派生类赋值给基类的过程,就是成员赋值的过程
void CallUp3(Phone phone) {
std::cout << "CallUp3(Phone phone)" << std::endl;
phone.CallUp();
}
int main() {
ApplePhone apple;
MiPhone mi;
OppoPhone oppo;
CallUp1(apple);
CallUp1(mi);
CallUp1(oppo);
std::cout << std::endl;
CallUp2(&apple);
CallUp2(&mi);
CallUp2(&oppo);
std::cout << std::endl;
CallUp3(apple);
CallUp3(mi);
CallUp3(oppo);
std::cout << std::endl;
return 0;
}
代码运行结果如下,可以看到,调用 CallUp1() 和 CallUp2() 能体现出多态的特点,分别调用的是类自己的 CallUp();CallUp3() 没有体现多态,调用的都是基类的虚函数。引用传递和指针传递都能体现多态的特点;值传递不能体现多态的特点,还是调用的基类中的函数。
1.2 重载
重载的示例代码如下,有以下几点需要注意:
(1)形参列表相同,返回值不同,不能重载
(2)形参列表的类型一样,但是形参名不一样,比如 (int a, int b) 和 (int b, int a),不构成重载
(3)对于实参是小数的,编译器默认为 double 类型的,如果函数的形参没有 double 类型,只有 float,int,那么编译器找不到 double 形参的时候会尝试转化成 float 或者 int,这样会产生二义性,编译错误。
#include <iostream>
#include <string>
class Test {
public:
void Print(int a) {
std::cout << "int a = " << a << std::endl;
}
void Print(double a) {
std::cout << "double a = " << a << std::endl;
}
// g++ 中小数默认是 double 数据类型的
// 如果调用的时候,不使用 float 做数据类型转换
// 会和 Print(int a, int b) 产生二义性
void Print(float a, float b) {
std::cout << "float a = " << a << ", float b = " << b << std::endl;
}
void Print(char c) {
std::cout << "int c = " << c << std::endl;
}
void Print(int a, char c) {
std::cout << "int a = " << a << ", int c = " << c << std::endl;
}
void Print(char c, int a) {
std::cout << "char c = " << c << ", int a = " << a << std::endl;
}
// int Print(int a) {
// std::cout << "return int, int a = " << a << std::endl;
// }
void Print(int a, int b) {
std::cout << "int a = " << a << ", int b = " << b <<std::endl;
}
// void Print(int b, int a) {
// std::cout << "int b = " << b << ", int a = " << a << std::endl;
// }
};
int main() {
Test t;
t.Print(10);
t.Print(10.0);
t.Print((float)10.0, (float)10.0);
t.Print('a');
t.Print(10, 'a');
t.Print('a', 10);
t.Print(10, 20);
t.Print(20, 20);
return 0;
}
1.2.1 c++ 为什么能重载,而 c 语言不能重载
c++ 中的函数符号包括了形参信息,所以不同的形参在 c++ 编译的目标文件中是不同的符号,所以, c++ 可以重载;c 语言中的函数符号不包括形参信息,如果重载的话就会产生符号冲突,所以不支持重载。
使用 objdump -tT aout 查看符号表,过滤 Print,可以看到如下符号。函数名相同,形参列表不同,生成的符号是不一样的。
使用 objdump -d -S -l a.out 查看反汇编结果,可以看到,main()书中调用了不同的符号。
如下的代码使用 gcc 是编译不过的,使用 g++ 可以编译通过。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void Print(int a) {
printf("int a = %d\n", a);
}
void Print(int a, int b) {
printf("int a = %d, int b = %d\n", a, b);
}
int main() {
Print(10);
Print(10, 10);
return 0;
}
extern "C":
extern "C" 在 c++ 代码中使用,告诉编译器按 c 的风格来编译。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern "C" {
void Print(int a, int b) {
printf("int a = %d, int b = %d\n", a, b);
}
}
void Print(char a, char b) {
printf("char a = %d, char b = %d\n", a, b);
}
int main() {
Print(10, 10);
Print('a', 'b');
return 0;
}
可以看到被 extern "C" 修饰的函数符号中不包含形参信息,是 c 的风格;没有被 extern "C" 修饰的函数符号中包含形参信息,是 c++ 的风格。
1.2.2 重载是编译多态,虚函数是运行多态
由上边的分析可以知道,函数重载,具体调用哪个函数,在编译的时候就确定了,所以可以称为编译时多态,是静态的。
虚函数直到程序运行的时候才确定调用哪个函数,所以称为运行时多态,是动态的。
2 虚函数和虚函数表
2.1 函数名即地址
在 c 和 c++ 中函数和全局变量类似,都是有地址的,函数名就表示函数的入口地址。
如下代码,声明了一个函数指针 PF,形参和返回值与 Hello 是一致的,这样就可以直接通过 pf 来对 Hello 做函数调用了。
#include <iostream>
#include <string>
void Hello(std::string name) {
std::cout<< "hello " << name << std::endl;
}
typedef void (*PF)(std::string name);
int main() {
std::cout<< (void *)Hello << std::endl;
Hello("marry");
PF pf = Hello;
std::cout << "pf = " << (void *)pf << std::endl;
pf("tom");
return 0;
}
2.2 虚表
虚函数表是虚函数工作的基础,也是实现多态的核心。对于有虚函数的类来说,都会有一个虚函数表。如下图所示,有虚函数的类,这个类的每个对象的第一个 8 字节(64 位系统是 8 字节,32 位系统是 4 字节) 中保存的是虚函数表的地址。
如果一个有虚函数的类,没有其它成员变量,那么 sizeof() 计算这个类的大小是 8(64 位系统上),8 字节也就是存放虚函数表的空间。
#include <iostream>
#include <string>
class Base {
public:
virtual void SayHello1() {
std::cout << "hello1" << std::endl;
}
};
int main() {
std::cout << "sizeof(Base) = " << sizeof(Base) << std::endl;
return 0;
}
一个类的所有对象共用一张虚函数表,所以每个对象的第一个 8 字节里边存储的数据内容是相等的,都是虚函数表的地址。虚表中保存的是函数的地址,而函数又属于代码段,代码段是共享的,所以一个类的对象共享虚表。
如下代码,有一个包含虚函数的类 Base,b1 和 b2 都是 Base 的对象,我们获取对象第一个 8 字节中的内容,发现 b1 和 b2 是相等的,这也验证了一个类的对象指向同一个虚表。
#include <iostream>
#include <string>
class Base {
public:
virtual void SayHello1() {
std::cout << "hello1" << std::endl;
}
virtual void SayHello2() {
std::cout << "hello2" << std::endl;
}
virtual void SayHello3() {
std::cout << "hello3" << std::endl;
}
};
int main() {
Base b1;
Base b2;
printf("&b1 = %p, *(long *)(&b1) = 0x%llx\n", &b1, *(long *)(&b1));
printf("&b1 = %p, *(long *)(&b2) = 0x%llx\n", &b2, *(long *)(&b2));
return 0;
}
运行结果如下,从打印信息来看,虚函数表的地址是 0x55f2dd2dbd58。
虚函数表中每一个表项都是一个函数的地址,在 64 位机器上,一个函数地址的长度是 8 字节,所以一个表项占 8 字节。如下代码,在类 Base 中有 3 个虚函数,另外定义了一个函数指针类型。在 main 函数中首先找到了虚函数表,然后依次取出了虚函数的地址,通过函数地址可以直接进行调用。
#include <iostream>
#include <string>
class Base {
public:
virtual void SayHello1() {
std::cout << "hello1" << std::endl;
}
virtual void SayHello2() {
std::cout << "hello2" << std::endl;
}
virtual void SayHello3() {
std::cout << "hello3" << std::endl;
}
};
// 定义一个函数指针类型
typedef void (*PF)(void);
int main() {
std::cout << "sizeof(Base) = " << sizeof(Base) << std::endl;
Base b;
// vfptr 是指向虚函数表的地址
long *vfptr = (long *)*(long *)(&b);
// 虚函数表的第一个 8 字节就是函数 SayHello1 的地址
long f1_function = *(vfptr + 0);
// 虚函数表的第二个 8 字节是函数 SayHello2 的地址
long f2_function = *(vfptr + 1);
// 虚函数表的第三个 8 字节是函数 SayHello3 的地址
long f3_function = *(vfptr + 2);
printf("function1 = %p\n", f1_function);
printf("function2 = %p\n", f2_function);
printf("function3 = %p\n", f3_function);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
return 0;
}
运行结果如下:
2.3 单继承,没有虚函数覆盖
如下代码, Base 中有 3 个虚函数,Derived 继承了 Base 类, Derived 中有 2 个虚函数。Derived 中没有覆盖 Base 中的函数,所以 Derived 的虚表有 5 个表项,Base 中的虚表有 3 个表项。
#include <iostream>
#include <string>
class Base {
public:
virtual void faa1() {
std::cout << "faa1" << std::endl;
}
virtual void faa2() {
std::cout << "faa2" << std::endl;
}
virtual void faa3() {
std::cout << "faa3" << std::endl;
}
};
class Derived : public Base{
public:
virtual void fbb1() {
std::cout << "fbb1" << std::endl;
}
virtual void fbb2() {
std::cout << "fbb2" << std::endl;
}
};
typedef void (*PF)(void);
void CallPF1() {
// Base b; // Base 中的虚表只有 3 个函数
// 直接对象赋值,b 中的虚表也是只用 3 个表项
// Derived d;
// Base b = d;
Base b;
long *vfptr = (long *)*(long *)(&b);
long f1_function = *(vfptr + 0);
long f2_function = *(vfptr + 1);
long f3_function = *(vfptr + 2);
long f4_function = *(vfptr + 3);
long f5_function = *(vfptr + 4);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
pf = (PF)(f4_function);
pf();
pf = (PF)(f5_function);
pf();
}
void CallPF2() {
Derived b;
long *vfptr = (long *)*(long *)(&b);
long f1_function = *(vfptr + 0);
long f2_function = *(vfptr + 1);
long f3_function = *(vfptr + 2);
long f4_function = *(vfptr + 3);
long f5_function = *(vfptr + 4);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
pf = (PF)(f4_function);
pf();
pf = (PF)(f5_function);
pf();
}
void CallPF3() {
Base *b = new Derived();
long *vfptr = (long *)*(long *)(b);
long f1_function = *(vfptr + 0);
long f2_function = *(vfptr + 1);
long f3_function = *(vfptr + 2);
long f4_function = *(vfptr + 3);
long f5_function = *(vfptr + 4);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
pf = (PF)(f4_function);
pf();
pf = (PF)(f5_function);
pf();
}
int main() {
std::cout << "sizeof(Base) = " << sizeof(Base) << std::endl;
std::cout << "sizeof(Derived) = " << sizeof(Derived) << std::endl;
CallPF2();
std::cout << std::endl;
CallPF3();
std::cout << std::endl;
CallPF1();
std::cout << std::endl;
return 0;
}
运行结果如下,因为 Base 中的虚函数表的表项还是 3 个,所以调用 f4_function 和 f5_function 的时候会出现段错误。
2.4 单继承,有虚函数覆盖 --> 表项替换
在子类中,可以覆盖父类的虚函数,覆盖虚函数就是将虚函数表中的表项覆盖,改成子类中的虚函数的地址。当调用成员函数的时候,如果是虚函数,那么会查虚函数表,找到表之后再调用对应的函数。不同的子类,使用自己的虚函数地址覆盖了虚函数表的表项,那么函数调用的时候当然是调用子类中的函数。
如下代码,Base 类中有 3 个虚函数;Derived 继承了 Base,覆盖了 Base 中的 faa1 和 faa2;Derived1 继承了 Derived,覆盖了 faa1 和 faa2。
#include <iostream>
#include <string>
class Base {
public:
virtual void faa1() {
std::cout << "faa1" << std::endl;
}
virtual void faa2() {
std::cout << "faa2" << std::endl;
}
virtual void faa3() {
std::cout << "faa3" << std::endl;
}
};
class Derived : public Base{
public:
virtual void faa1() {
std::cout << "derived faa1" << std::endl;
}
void faa2() {
std::cout << "derived faa2" << std::endl;
}
virtual void fbb1() {
std::cout << "fbb1" << std::endl;
}
virtual void fbb2() {
std::cout << "fbb2" << std::endl;
}
};
class Derived1 : public Derived {
public:
void faa1() {
std::cout << "Derived1 faa1" << std::endl;
}
void fbb1() {
std::cout << "Derived1 fbb1" << std::endl;
}
};
typedef void (*PF)(void);
void CallPF1() {
// Base b; // Base 中的虚表只有 3 个函数
// 直接对象赋值,b 中的虚表也是只用 3 个表项
// Derived d;
// Base b = d;
Base b;
long *vfptr = (long *)*(long *)(&b);
long f1_function = *(vfptr + 0);
long f2_function = *(vfptr + 1);
long f3_function = *(vfptr + 2);
long f4_function = *(vfptr + 3);
long f5_function = *(vfptr + 4);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
pf = (PF)(f4_function);
pf();
pf = (PF)(f5_function);
pf();
}
void CallPF2() {
Derived b;
long *vfptr = (long *)*(long *)(&b);
long f1_function = *(vfptr + 0);
long f2_function = *(vfptr + 1);
long f3_function = *(vfptr + 2);
long f4_function = *(vfptr + 3);
long f5_function = *(vfptr + 4);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
pf = (PF)(f4_function);
pf();
pf = (PF)(f5_function);
pf();
}
void CallPF3() {
Derived1 b;
long *vfptr = (long *)*(long *)(&b);
long f1_function = *(vfptr + 0);
long f2_function = *(vfptr + 1);
long f3_function = *(vfptr + 2);
long f4_function = *(vfptr + 3);
long f5_function = *(vfptr + 4);
PF pf;
pf = (PF)(f1_function);
pf();
pf = (PF)(f2_function);
pf();
pf = (PF)(f3_function);
pf();
pf = (PF)(f4_function);
pf();
pf = (PF)(f5_function);
pf();
}
int main() {
std::cout << "sizeof(Base) = " << sizeof(Base) << std::endl;
std::cout << "sizeof(Derived) = " << sizeof(Derived) << std::endl;
CallPF2();
std::cout << std::endl;
CallPF3();
std::cout << std::endl;
CallPF1();
std::cout << std::endl;
return 0;
}
运行结果如下:
2.5 多继承
如下代码,有 3 个基类,Base1,Base2 和 Base3。3 个基类中都有虚函数,也都有一个虚函数表。Derived 类继承 3 个基类,那么 Derived 中就有 3 个虚函数表。从打印的 Derived 的大小也能看出来,Base1、Base2、Base3 的大小是 8,Derived 的大小是 24。
#include <iostream>
#include <string>
class Base1 {
public:
virtual void foo1() {
std::cout << "Base1::foo1" << std::endl;
}
virtual void faa2() {
std::cout << "Base1::faa2" << std::endl;
}
virtual void faa3() {
std::cout << "Base1::faa3" << std::endl;
}
};
class Base2 {
public:
virtual void foo1() {
std::cout << "Base2::foo1" << std::endl;
}
virtual void fbb2() {
std::cout << "Base2::fbb2" << std::endl;
}
virtual void fbb3() {
std::cout << "Base2::fbb3" << std::endl;
}
};
class Base3 {
public:
virtual void foo1() {
std::cout << "Base3::foo1" << std::endl;
}
virtual void fcc2() {
std::cout << "Base3::fcc2" << std::endl;
}
virtual void fcc3() {
std::cout << "Base3::fcc3" << std::endl;
}
};
class Derived : public Base1, public Base2, public Base3 {
public:
virtual void fbb1() {
std::cout << "Derived::fbb1" << std::endl;
}
void foo1() {
std::cout << "Derived::foo1" << std::endl;
}
void fbb2() {
std::cout << "Derived::fbb2" << std::endl;
}
};
int main() {
std::cout << "sizeof(Base1) = " << sizeof(Base1) << std::endl; // 8
std::cout << "sizeof(Base2) = " << sizeof(Base2) << std::endl; // 8
std::cout << "sizeof(Base3) = " << sizeof(Base3) << std::endl; // 8
std::cout << "sizeof(Derived) = " << sizeof(Derived) << std::endl; // 24
Derived d;
d.foo1();
d.fbb2();
d.fcc2();
return 0;
}
运行结果如下:
2.6 一个类的多个对象是不是共享一个虚函数表 ?
是,一个包含虚函数的类的多个对象共享一个虚函数表。
如下代码,Base 是基类,Derived 是派生类。两个类分别有自己的虚函数表,对于一个类的多个对象来说,多个对象共享这个类的虚函数表。
对于一个对象来说,第一个 8 字节保存的就是虚函数表的地址,代码中打印出了每个对象的虚函数表。
#include <iostream>
#include <string>
class Base {
public:
Base() {
std::cout << "Base()" << std::endl;
}
~Base() {
std::cout << "~Base()" << std::endl;
}
virtual void Do() {
std::cout << "Base() Do()" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived()" << std::endl;
}
~Derived() {
std::cout << "~Derived()" << std::endl;
}
void Do() {
std::cout << "Derived() Do()" << std::endl;
}
};
typedef void (*PF)(void);
void TestBase() {
Base b1;
Base b2;
Base b3;
long *vfptr1 = (long *)*(long *)(&b1);
long *vfptr2 = (long *)*(long *)(&b2);
long *vfptr3 = (long *)*(long *)(&b3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
void TestDerived() {
Derived d1;
Derived d2;
Derived d3;
long *vfptr1 = (long *)*(long *)(&d1);
long *vfptr2 = (long *)*(long *)(&d2);
long *vfptr3 = (long *)*(long *)(&d3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
void TestDerivedBase() {
Base *d1 = new Derived;
Base *d2 = new Derived;
Base *d3 = new Derived;
long *vfptr1 = (long *)*(long *)(d1);
long *vfptr2 = (long *)*(long *)(d2);
long *vfptr3 = (long *)*(long *)(d3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
int main() {
std::cout << "Base ------------------------" << std::endl;
TestBase();
std::cout << "Derived ---------------------" << std::endl;
TestDerived();
std::cout << "DerivedBase -----------------" << std::endl;
TestDerivedBase();
return 0;
}
打印结果如下:
(1)对于同一个类的多个对象来说,虚函数表是相同的
(2)基类和派生类的虚函数表是不同的,各自有各自的虚函数表,但是把派生类指针或者引用赋值给基类指针或者基类引用,那么基类中的虚函数表是派生类中的虚函数表
2.7 一个基类的多个派生类之间是共享一个虚表吗 ?
不是,虚函数表是以类为单位而存在的,一个基类的多个派生类分别有各自的虚函数表。
如下代码,Base 是基类,有 3 个派生类 Derived1,Derived2 和 Derived3。分别打印了 3 个派生类的虚函数表,虚函数表是不一样的。
#include <iostream>
#include <string>
class Base {
public:
Base() {
std::cout << "Base()" << std::endl;
}
~Base() {
std::cout << "~Base()" << std::endl;
}
virtual void Do() {
std::cout << "Base() Do()" << std::endl;
}
};
class Derived1 : public Base {
public:
Derived1() {
std::cout << "Derived1()" << std::endl;
}
~Derived1() {
std::cout << "~Derived1()" << std::endl;
}
void Do() {
std::cout << "Derived1() Do()" << std::endl;
}
};
class Derived2 : public Base {
public:
Derived2() {
std::cout << "Derived2()" << std::endl;
}
~Derived2() {
std::cout << "~Derived2()" << std::endl;
}
void Do() {
std::cout << "Derived2() Do()" << std::endl;
}
};
class Derived3 : public Base {
public:
Derived3() {
std::cout << "Derived3()" << std::endl;
}
~Derived3() {
std::cout << "~Derived3()" << std::endl;
}
void Do() {
std::cout << "Derived3() Do()" << std::endl;
}
};
typedef void (*PF)(void);
void TestBase() {
Base b1;
Base b2;
Base b3;
long *vfptr1 = (long *)*(long *)(&b1);
long *vfptr2 = (long *)*(long *)(&b2);
long *vfptr3 = (long *)*(long *)(&b3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
void TestDerived1() {
Derived1 d1;
Derived1 d2;
Derived1 d3;
long *vfptr1 = (long *)*(long *)(&d1);
long *vfptr2 = (long *)*(long *)(&d2);
long *vfptr3 = (long *)*(long *)(&d3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
void TestDerived2() {
Derived2 d1;
Derived2 d2;
Derived2 d3;
long *vfptr1 = (long *)*(long *)(&d1);
long *vfptr2 = (long *)*(long *)(&d2);
long *vfptr3 = (long *)*(long *)(&d3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
void TestDerived3() {
Derived3 d1;
Derived3 d2;
Derived3 d3;
long *vfptr1 = (long *)*(long *)(&d1);
long *vfptr2 = (long *)*(long *)(&d2);
long *vfptr3 = (long *)*(long *)(&d3);
std::cout << "1, virtual function table addr " << vfptr1 << std::endl;
std::cout << "2, virtual function table addr " << vfptr2 << std::endl;
std::cout << "3, virtual function table addr " << vfptr3 << std::endl;
long f1 = *(vfptr1 + 0);
long f2 = *(vfptr2 + 0);
long f3 = *(vfptr3 + 0);
PF pf1 = (PF)(f1);
PF pf2 = (PF)(f2);
PF pf3 = (PF)(f3);
pf1();
pf2();
pf3();
}
int main() {
std::cout << "Base ------------------------" << std::endl;
TestBase();
std::cout << "Derived1 ---------------------" << std::endl;
TestDerived1();
std::cout << "Derived2 ---------------------" << std::endl;
TestDerived2();
std::cout << "Derived3 ---------------------" << std::endl;
TestDerived3();
return 0;
}
运行结果如下:
Base ------------------------
Base()
Base()
Base()
1, virtual function table addr 0x5565ee1b8d08
2, virtual function table addr 0x5565ee1b8d08
3, virtual function table addr 0x5565ee1b8d08
Base() Do()
Base() Do()
Base() Do()
~Base()
~Base()
~Base()
Derived1 ---------------------
Base()
Derived1()
Base()
Derived1()
Base()
Derived1()
1, virtual function table addr 0x5565ee1b8cf0
2, virtual function table addr 0x5565ee1b8cf0
3, virtual function table addr 0x5565ee1b8cf0
Derived1() Do()
Derived1() Do()
Derived1() Do()
~Derived1()
~Base()
~Derived1()
~Base()
~Derived1()
~Base()
Derived2 ---------------------
Base()
Derived2()
Base()
Derived2()
Base()
Derived2()
1, virtual function table addr 0x5565ee1b8cd8
2, virtual function table addr 0x5565ee1b8cd8
3, virtual function table addr 0x5565ee1b8cd8
Derived2() Do()
Derived2() Do()
Derived2() Do()
~Derived2()
~Base()
~Derived2()
~Base()
~Derived2()
~Base()
Derived3 ---------------------
Base()
Derived3()
Base()
Derived3()
Base()
Derived3()
1, virtual function table addr 0x5565ee1b8cc0
2, virtual function table addr 0x5565ee1b8cc0
3, virtual function table addr 0x5565ee1b8cc0
Derived3() Do()
Derived3() Do()
Derived3() Do()
~Derived3()
~Base()
~Derived3()
~Base()
~Derived3()
~Base()