C++中多态的原理【精华】

虚函数表

通过一道题我们先感受一下编译器针对多态的处理

#include 
using namespace std;

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
	char _c
};

int main()
{
	cout << sizeof(Base) << endl;
	return 0;
}

在x64环境下,显示的结果是12字节
在这里插入图片描述

为什么是12字节呢?一个int就4字节,一个char就1字节,内存对齐一下也是8字节呀!为什么显示的结果是12字节呢? 我们调试代码,看看内部结构
C++中多态的原理【精华】_第1张图片
通过调试我们发现Base创建的对象里面有一个指针_vfptr,如果我们计算sizeof的时候把这个指针算上的话刚刚好的12字节!

那么疑问来了,这个指针到底是什么?为什么会出现在对象里面?这个指针到底有什么作用?

这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表,。那么子类中这个表放了些什么呢?我们接着往下分析

#include 
using namespace std;

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}

	virtual void Func2()
	{
		cout << "Func2()" << endl;
	}

	void Func3()
	{
		cout << "Func3()" << endl;
	}
private:
	int _b = 1;
	char _c;
};

int main()
{
	cout << sizeof(Base) << endl;
	Base b;
	return 0;
}

通过监视窗口再来看:
C++中多态的原理【精华】_第2张图片
我们发现这个指针指向一个数组(虚函数表),这个数组(表)里面存的就是虚函数的地址


知道了包含虚函数的类里面会出现一个指针,这个指针指向一个指针数组,数组里面保存的是类里面的虚函数,接下来我们看看多态的原理

文章目录

  • 虚函数表
  • 多态的原理


多态的原理

提示:这里可以添加本文要记录的大概内容:

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl; }
 virtual void func(){}
private:
	int _a = 1; 
};

class Student : public Person {
public:
 virtual void BuyTicket() { cout << "买票-半价" << endl; }
private:
	int _b = 0; 
};

void Func(Person& p)
{
	p.BuyTicket();
}

int main()
{
	Person p;
	Student s;
	Func(p);
	Func(s);
 	return 0;
}

我们看一下父类的虚表指针
C++中多态的原理【精华】_第3张图片
我们看到Person对象里面的_vfptr指向的指针数组里面有两个指针,说明保存的是两个地址,我们看看这两个地址是什么?
C++中多态的原理【精华】_第4张图片
然后我们看一下_vfptr指针指向的数组里面保存的地址
C++中多态的原理【精华】_第5张图片
再看一下子类的虚表指针
C++中多态的原理【精华】_第6张图片
虚函数需要被重写,一开始子类会将虚函数进行直接拷贝,重写虚函数之后,虚函数的地址就发生改变了

那为什么多态的原理和指向的对象有关?和虚表有什么关系?

C++中多态的原理【精华】_第7张图片

父类对象找到虚函数表的地址,在表里面找到虚函数的地址,因为这里是父类对象,直接调用父类对象的虚函数
子类对象赋值给父类对象,这样会产生“切片”,找到子类中父类的那一部分,寻找到虚函数表的地址,但是虚函数表里存的是子类重写的父类虚函数的地址,所以此时_vfptr指向的其实是子类的虚函数指针数组,自认而然掉用的是子类的虚函数

那为什么不使用指针和引用后,就不构成多态了呢?

#include 
#include 
#include 
using namespace std;

class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
	virtual void func() {}
private:
	int _a = 1;
};

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
private:
	int _b = 0;
};

void Func(Person p)
{
	p.BuyTicket();
}

int main()
{
	Person p;
	Student s;
	Func(p);
	Func(s);
	return 0;
}

C++中多态的原理【精华】_第8张图片

因为指针和引用是指向子类对象切割出来的父类对象的那一部分
不使用指针和引用后,直接将子类传递给父类,切割出来的子类对象中父类的那一部分,成员拷贝给父类,但是不会拷贝虚表指针

你可能感兴趣的:(C++,c++)