C++三大特性—多态 “抽象类与虚函数表”

这篇具有很好参考价值的文章主要介绍了C++三大特性—多态 “抽象类与虚函数表”。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

抽象类和虚函数表是 C++中实现多态性的重要概念,它们对于学习 C++非常重要。
掌握抽象类和虚函数表的使用方法对于理解 C++的多态性是非常重要的。在 C++中,通过使用抽象类和虚函数表,可以实现基于多态性的各种功能,如继承、多态、模板等。同时,在实际应用中,抽象类和虚函数表也是常用的设计模式之一,如抽象工厂模式、观察者模式等。

抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

 和普通的虚函数不一样,一个纯虚函数无需定义。而且 =0 只能出现在类内部的虚函数声明处。我们也可以为虚函数提供定义,不过函数体必须定义在类的外部。也就是说,我们不能在类的内部为一个纯虚函数提供函数体。

class Person
{
public:
	virtual void work() = 0;//纯虚函数
};
class Student : public Person
{
public:
	virtual void work()
	{
		cout << "Student-学生上学" << endl;
	}
};
class Teacher : public Person
{
public:
	virtual void work()
	{
		cout << "Teacher-老师教书" << endl;
	}
};
	Person obj;       //错误写法,Person类中声明了纯虚函数,不能定义Person的对象
	Student obj_stu;  //正确写法
	Teacher obj_per;  //正确写法

 派生类继承基类的纯虚函数,如果不重写虚函数,那么这个派生类仍然是抽象类。所以必须对其进行重新定义(重写)才能实例出对象:
C++三大特性—多态 “抽象类与虚函数表”

接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。


虚函数表

在了解虚函数表之前我们先来看一道常见的面试题:

//sizeof(Base)是多少?
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
	char _ch;
};

我们很可能会简单的以为只是常见的类的内存对齐问题,认为结果是8,但是结果是:
C++三大特性—多态 “抽象类与虚函数表”
为什么结果是12呢?原因就是有虚函数表的存在。
C++三大特性—多态 “抽象类与虚函数表”
C++三大特性—多态 “抽象类与虚函数表”

(12的原因是vs的X86默认对齐数码是根据平台的字长(即指针的大小)来确定的,也就是4。换为Linux环境则可能为8)

注意:我们要理清楚虚函数表(虚表)与虚基表的区别,不要搞混(虚基表)


那么派生类重写(覆盖)基类的虚函数,虚函数表又是以什么样的形式变化呢?

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
	char _ch;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

C++三大特性—多态 “抽象类与虚函数表”

完成重写的虚函数虚表对应的位置覆盖成重写的虚函数。、


那么虚函数表是存放在哪个区的呢?栈?堆?静态区?常量区?
我们通过下面代码可以大致猜测一下:

int main()
{
	int a = 0;
	cout << "栈:" << &a << endl;

	int* p1 = new int;
	cout << "堆:" << p1 << endl;

	const char* str = "hello world";
	cout << "代码段/常量区:" << (void*)str << endl;

	static int b = 0;
	cout << "静态区/数据段:" << &b << endl;

	Base be;
	cout << "虚表:" << (void*)*((int*)&be) << endl;
	return 0;
}

C++三大特性—多态 “抽象类与虚函数表”
运行结果:C++三大特性—多态 “抽象类与虚函数表”
所以我们猜测虚表应该存放在静态区
这里给大家提供一个打印虚表的办法:

typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
	// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();
 }
 cout << endl;
}
int main()
{
	Base b;
	Derive d;
	VFPTR * vTableb = (VFPTR*)(*(int*)&b);
	PrintVTable(vTableb);
	VFPTR* vTabled = (VFPTR*)(*(int*)&d);
	PrintVTable(vTabled);
	return 0;
}

思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr
1.先取b的地址,强转成一个 int * 的指针
2.再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针
3.再强转成 VFPTR *,因为虚表就是一个存VFPTR类型(自己重定义的虚函数指针类型)的数组。
4.虚表指针传递给PrintVTable进行打印虚表
5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面没有放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的 - 生成 - 清理解决方案,再编译就好了。

C++三大特性—多态 “抽象类与虚函数表”

C++三大特性—多态 “抽象类与虚函数表”

C++三大特性—多态 “抽象类与虚函数表”
相同类型的对象共用一个虚表


多继承关系的虚函数表

且看下面代码分析:

class Base1 {
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1;
};
class Base2 {
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2;
};
class Derive : public Base1, public Base2 {
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1;
};

主要的关系就是Derive同时继承Base1与Base2 ,Derive重写了func1但没有重写func2,且自生还有一个func为虚函数。

多继承虚函数表:
C++三大特性—多态 “抽象类与虚函数表”
我们使用上面说过的打印虚表的方法:

	Derive d;
	VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
	PrintVTable(vTableb1);
	VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
	PrintVTable(vTableb2);

C++三大特性—多态 “抽象类与虚函数表”
C++三大特性—多态 “抽象类与虚函数表”


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀文章来源地址https://www.toymoban.com/news/detail-464873.html

到了这里,关于C++三大特性—多态 “抽象类与虚函数表”的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++核心编程—类和对象,类的三大特性——封装、继承、多态

    纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。 ①什么是对象? 生活中有各种各样的 事物 ,如人、动物、植物等在C++中将这些称为对象。 对象多种多样, 各种对象的属性也不相同 。 例如狗的品种,毛色,年龄等 各个对象都有自己的行为 ,例

    2024年02月07日
    浏览(64)
  • 多态与虚函数(补)

    我们首先看下面一段代码 大家可以试着画一下上面三个类的虚表,此处我就不画了 我们试着想一下在上面这三个类型的基础上运行一下代码是否可以运行通过 有的同学可能会说这是可以运行通过的,因为我们的op指向了test对象,这就是错误的理解,该程序在编译时期就会出

    2024年02月05日
    浏览(34)
  • [C++] 多态(上) -- 抽象类、虚函数、虚函数表

    通俗来说,多态就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 举个栗子:比如 买票这个行为: 当 普通人 买票时,是全价买票; 学生 买票时,是半价买票; 军人 买票时是优先买票。 这个例子就是多态,不同身份对应不同的票

    2024年02月04日
    浏览(38)
  • C++——多态与虚表

    目录 1.多态的实现 2.虚表 2.1虚函数重写是怎么实现的 2.2多态的原理 2.3静态绑定与动态绑定 3.单继承体系中的虚函数表 ​编辑4.多继承体系中的虚函数表 5.菱形继承的虚函数表 6.菱形虚拟继承的虚函数表 在C++中,要想实现多态,必须满足以下几个条件: 有继承关系 有虚函数

    2024年02月07日
    浏览(39)
  • 【C++入门到精通】C++入门 —— 多态(抽象类和虚函数的魅力)

    前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,也了解了C++中的模版,以及学习了几个STL的结构也相信大家都掌握

    2024年02月11日
    浏览(43)
  • C++类和对象-多态->多态的基本语法、多态的原理剖析、纯虚函数和抽象类、虚析构和纯虚析构

    #includeiostream using namespace std; //多态 //动物类 class Animal { public:     //Speak函数就是虚函数     //函数前面加上virtual,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。     virtual void speak()     {         cout \\\"动物在说话\\\" endl;     } }; //猫类 class Cat

    2024年02月20日
    浏览(38)
  • Java三大特性:封装、继承、多态

    高内聚,低耦合 高内聚:类内部操作自己完成,不允许外部干涉。 低耦合:仅暴露少量的方法给外部使用。 封装(数据的隐藏) 通常应禁止直接访问一个对象中数据的实际表达,而应该通过操作接口来访问,这称为信息的隐藏。 封装的特点 1.提高程序的安全性,保护数据

    2024年03月21日
    浏览(47)
  • 【面向对象语言三大特性之 “多态”】

    目录  1. 多态的概念 1.1 概念 2. 多态的定义及实现 2.1多态的构成条件 2.2 虚函数  2.3虚函数的重写 2.4 C++11 override 和 final  2.5 重载、覆盖(重写)、隐藏(重定义)的对比  3. 抽象类 3.1 概念 3.2 接口继承和实现继承  4.多态的原理 4.1虚函数表  4.2多态的原理 4.3 动态绑定与静态绑定

    2023年04月17日
    浏览(100)
  • 【java】面向对象三大特性之多态

            俗话说的好,“一龙生九子,九子各不同”,这句话就蕴含了面向对象三大特性之一的多态的思想。那么多态具体有什么特点呢,就由博主来带大家梳理一下吧🤔 目录 一、什么是多态 二、重写 三、向上转型和向下转型 1、向上转型 2、向下转型 四、多态的优缺点

    2024年03月15日
    浏览(76)
  • 【JAVASE】带你了解面向对象三大特性之一(多态)

    ✅作者简介:大家好,我是橘橙黄又青,一个想要与大家共同进步的男人😉😉 🍎个人主页:再无B~U~G-CSDN博客 多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状 态。 总的来说:同一件事情,发生在不同对象

    2024年04月14日
    浏览(73)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包