前言:工作了小半年了,准备开个系列的总结笔记,主要从虚函数、const、回调函数、类的构建与析构特点、结构体中指针构造等方面出发,慢慢总结进步~
虚函数
先贴上自己学习理解参考的链接
ref1:https://blog.csdn.net/haoel/article/details/1948051 虚函数
ref2: https://blog.csdn.net/weixin_43329614/article/details/89103574 虚函数
ref3: https://blog.csdn.net/tonychan129/article/details/21294879 模板技术
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术(编译时决议),RTTI技术(运行时决议),虚函数技术(运行时决议),要么是试图做到在编译时决议,要么试图做到运行时决议。
虚函数的实现是由两个部分组成的,虚函数指针与虚函数表。虚函数继承时必须保持与父类一样的类型和参数以及修饰符【const情况下,如果子类没有加const,则算是重载】
1. 虚函数指针和虚函数表
在一个实例化的类的对象中,如果存在虚函数,则在该对象的地址首位后,紧跟虚函数表指针,这个表内存放了该类所有的虚函数指针。
假设有一个类Base,里面有如下的虚函数:
class Base
{
public:
virtual void f() { ; }
virtual void g() { ; }
virtual void h() { ; }
};
若实例化一个对象b, 则b的地址和内容对应如下【虚函数后面有个点,代表虚函数的结束符,类似‘\0’】:
此外我们可以通过如下代码来访问具体虚函数:我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)
typedef void(*Fun)(void);
int main()
{
Base b;
Fun pFun = NULL;
//访问第一个虚函数
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
pFun = (Fun) * ((int*)*(int*)(&b));
pFun();
//访问其他虚函数
cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1<< endl;
pFun = (Fun) * ((int*)*(int*)(&b) + 1);
pFun();
return 0;
}
输出如下【顺带可以看到指针值加了4(int型指针)】:
2.一般继承(无虚函数覆盖)
假设有一个类Derived继承Base,具体实现如下:
class Derived:public Base
{
public:
virtual void f1() { ; }
virtual void g1() { ; }
virtual void h1() { ; }
};
假设有如上所示的继承关系,类Derive继承了类Base,并且未重载了Base的任何函数,则类Derived的虚函数表如下【继承类的虚函数在基类的后面按声明依次排序】:
通过如下代码打印虚函数表来验证:
// 表示用户自己定义了一个函数指针数据类型
//typedef 返回类型(*新类型)(参数表)
typedef void(*Fun)(void);
int main()
{
Derived b;
Fun pFun = NULL;
// 表示定义了一个函数指针pFun ,改函数指针指向类似于void *pFun(void)的函数
//访问第一个虚函数
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
//&b取地址,int*将其转换成整型指针,*取对应内容,将内容转换成整型指针,最后转换成Fun类型指针,Fun
pFun = (Fun) * ((int*)*(int*)(&b));
pFun();
//访问其他虚函数
cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1<< endl;
pFun = (Fun) * ((int*)*(int*)(&b) + 1);
pFun();
//访问其他虚函数
cout << "虚函数表 — 第四个函数地址:" << (int*)*(int*)(&b) + 3 << endl;
pFun = (Fun) * ((int*)*(int*)(&b) + 3);
pFun();
return 0;
}
3.一般继承(有虚函数覆盖)
假设有一个类Derived继承Base,具体实现如下:
class Derived:public Base
{
public:
virtual void f() { cout << "Derived::f1" << endl; }
virtual void g1() { cout << "Derived::g1" << endl; }
virtual void h1() { cout << "Derived::h1" << endl; }
};
即:
此时函数的虚函数表如下:
通过如下代码打印虚函数表来验证,可以看出【 1.覆盖的f函数被放到了虚表中原来父类虚函数的位置。 2.没有被覆盖的函数依旧】
3.多重继承(无虚函数覆盖)
class Base1
{
public:
virtual void f() { cout << "Base1::f" << endl; }
virtual void g() { cout << "Base1::g" << endl; }
virtual void h() { cout << "Base1::h" << endl;; }
};
class Base2
{
public:
virtual void f() { cout << "Base2::f" << endl; }
virtual void g() { cout << "Base2::g" << endl; }
virtual void h() { cout << "Base2::h" << endl;; }
};
class Base3
{
public:
virtual void f() { cout << "Base3::f" << endl; }
virtual void g() { cout << "Base3::g" << endl; }
virtual void h() { cout << "Base3::h" << endl;; }
};
class Derived:public Base1,public Base2, public Base3
{
public:
virtual void f1() { cout << "Derived::f1" << endl; }
virtual void g1() { cout << "Derived::g1" << endl; }
//virtual void h1() { cout << "Derived::h1" << endl; }
};
即:
此时虚函数表如下,
通过如下代码打印虚函数表来验证,
// 表示用户自己定义了一个函数指针数据类型
//typedef 返回类型(*新类型)(参数表)
typedef void(*Fun)(void);
int main()
{
Derived b;
Fun pFun = NULL;
// 表示定义了一个函数指针pFun ,改函数指针指向类似于void *pFun(void)的函数
//访问第一个虚函数
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
//&b取地址,int*将其转换成整型指针,*取对应内容,将内容转换成整型指针,最后转换成Fun类型指针,Fun
pFun = (Fun) * ((int*)*(int*)(&b));
pFun();
//访问其他虚函数
cout << "虚函数表 — 第二个函数地址:" << (int*)*(int*)(&b) + 1<< endl;
pFun = (Fun) * ((int*)*(int*)(&b) + 1);
pFun();
//访问其他虚函数
cout << "虚函数表 — 第四个函数地址:" << (int*)*(int*)(&b) + 3 << endl;
pFun = (Fun) * ((int*)*(int*)(&b) + 3);
pFun();
//访问第二个虚函数表
int* tmp = (int*)(&b) + 1;
cout << "虚函数表地址:" << tmp << endl;
cout << "NO2.虚函数表 — 第一个函数地址:" << (int*)*(tmp) << endl;
pFun = (Fun) * ((int*)*(tmp));
pFun();
//访问第三个虚函数表
int* tmp2 = (int*)(&b) + 2;
cout << "虚函数表地址:" << tmp2 << endl;
cout << "NO3.虚函数表 — 第一个函数地址:" << (int*)*(tmp2) << endl;
pFun = (Fun) * ((int*)*(tmp2));
pFun();
return 0;
}
可以看出,和虚函数表对应起来了
4.多重继承(有虚函数覆盖)
继承关系如图所示
此时虚函数表如下:
经过验证,可以看到对应虚函数都上所示被正确覆盖