【C++进阶】多态的理解

这篇具有很好参考价值的文章主要介绍了【C++进阶】多态的理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.多态是什么

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。

对于多态,不同的对象传过去,会调用不同的函数

即多态调用看的是指向的对象

//A,B类中的func函数是个多态
class A
{
public:
	virtual void func()
	{
		cout << "A->func" << endl;
	}
};

class B :public A
{
public:
	virtual void func()
	{
		cout << "B->func" << endl;
	}
};

动态绑定和静态绑定

多态分为两种:

        1.静态绑定,也称为静态多态,是在程序编译阶段确定的,例如:函数重载和模板

        2.动态绑定,也称为动态多态,是在程序运行阶段确定的,根据具体拿到的类型确定程序的               具体行为,调用具体的函数。


二.虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数

虚函数一般是存在代码段(常量区)的,可能不同的编译器会不一样。

纯虚函数与抽象类

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

抽象类的作用:抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。

【C++进阶】多态的理解,C++进阶,c++,开发语言,多态

接口继承和实现继承

实现继承:普通函数的继承是一种实现继承

接口继承:虚函数的继承是一种接口继承,如果不实现多态,不要把函数定义成虚函数。

虚函数与静态成员函数

静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表,即静态成员函数不能设置成虚函数

虚函数与 inline 函数

inline函数可以设置成虚函数,不过编译器就忽略inline属性,这个函数就不再是inline,因为虚函数要放到虚表中去。


三.多态的条件

虚函数重写(覆盖)条件

             a.是虚函数,即要有 virtual

             b.虚函数满足三同(返回值,函数名,参数列表相同)即构成重写

    例外:

             a.派生类可以不加 virtual  ,因为派生类已经继承了基类的 virtual;

             b.协变(基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象                    的指针或者引用时,称为协变)

构成条件

1.调用的函数是重写的虚函数

2.必须通过基类的指针或者引用调用虚函数

重写析构函数

其实编译后析构函数的名称统一处理成destructor,此时析构函数的函数名相同,参数列表也相同,再加上 virtual  修饰,此时就重写了基类和派生类中的析构函数,即构成了多态。

结论

析构函数建议设置成虚函数,因为有时可能利用多态方式通过基类指针调用子类析构函        数,尤其是父类的析构函数强力建议设置为虚函数,这样动态释放父类指针所指的子类      对象时,能够达到析构的多态。

重载,重定义(隐藏)与重写

重载:在同一作用域,函数名相同,返回值可以不同,参数列表必须不同

重定义(隐藏):在不同的作用域,一个在基类,一个在派生类,只要函数名相同就构成重定义

重写:1.在不同的作用域,一个在基类,一个在派生类;

           2.都必须是虚函数

           3.满足三同(函数名,返回值,参数列表相同(协变除外))

总结

1.重写比重定义的条件更加严苛;

2.两个基类和派生类的同名函数,不是重定义就是重写

class A
{
public:
	virtual void func()   //func函数重写
	{
		cout << "A->func" << endl;
	}
};

class B :public A
{
public:
	virtual void func()
	{
		cout << "B->func" << endl;
	}
};

void test(A& a)   //基类引用
{
	a.func();
}

int main()
{
	A a;
	B b;
	test(a);   //传A的对象,调用A类的函数
	test(b);   //传B的对象,调用B类的函数

	return 0;
}

   override 和 final

override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错;

final:修饰虚函数,表示该虚函数不能再被重写

class A
{
public:
    virtual void func1() final  {}   //func1不能被重写
    virtual void func2() {}
  
};

class B:public A
{
public:
    virtual void func2() override {}   //检查 func2 是否正确重写
};

四.多态的原理

虚函数表

其实基类和派生类的虚函数都会被放进虚函数表(简称虚表)里,类实例化出对象后会生成一个指针(_vfptr)指向虚函数表,其实虚函数表就是一个函数指针数组,里面存着虚函数的地址,一般情况这个数组最后面放了一个nullptr。

同一个类的对象共享一个虚表

【C++进阶】多态的理解,C++进阶,c++,开发语言,多态

 打印虚表

因为虚表指针一般存在对象的前4个字节(64位则为前8个字节),我们可以通过强制类型转换拿到这个虚表指针。

typedef void (*FUNC_PTR) ();    //重定义函数指针

void print(FUNC_PTR* table)
{
	for (size_t i = 0; table[i] != nullptr; i++)   //需要注意每次运行时最好重新生成以下解决方 
                                                   //案,因为多次编译vs可能就没有这个nullptr了
	{
		printf("%p : ", table[i]);

		FUNC_PTR f = table[i];
		f();   //调用函数指针所指向的函数
	}

	cout << endl;
}

class A
{
public:
	virtual void func1()
	{
		cout << "A->func1" << endl;
	}

	virtual void func2()
	{
		cout << "A->func2" << endl;
	}
};

int main()
{
	A a;
	int vft1 = *((int*)&a);
	print((FUNC_PTR*)vft1);
	return 0;
}

观察打印结果我们可以发现,打印的就是对象里的所有虚函数。

【C++进阶】多态的理解,C++进阶,c++,开发语言,多态

 

虚表生成

虚表指针其实是在初始化列表阶段初始化的,所以构造函数不能设置成虚函数;

虚表生成:

                  a.先将基类中的虚表内容拷贝一份到派生类虚表中

                  b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函                       数;

                  c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后


🐬🤖本篇文章到此就结束了, 若有错误或是建议的话,欢迎小伙伴们指出;🕊️👻

😄😆希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;🥰🤩

😍😁谢谢你的阅读。😸😼文章来源地址https://www.toymoban.com/news/detail-612488.html

到了这里,关于【C++进阶】多态的理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【C++进阶】继承、多态的详解(继承篇)

    作者:爱写代码的刚子 时间:2023.7.28 前言:本篇博客主要介绍C++进阶部分内容——继承,C++中的继承和多态是比较复杂的,需要我们认真去深挖其中的细节。 继承的概念及定义 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序

    2024年02月13日
    浏览(28)
  • C++类开发第七篇(详细说说多态和编译原理)

    多态性(polymorphism)提供接口与具体实现之间的另一层隔离,从而将”what”和”how”分离开来。多态性改善了代码的可读性和组织性,同时也使创建的程序具有可扩展性,项目不仅在最初创建时期可以扩展,而且当项目在需要有新的功能时也能扩展。 c++支持编译时多态(静态多

    2024年03月09日
    浏览(41)
  • c、c++、java、python、js对比【面向对象、过程;解释、编译语言;封装、继承、多态】

    目录 内存管理、适用 区别 C 手动内存管理:C语言没有内置的安全检查机制,容易出现内存泄漏、缓冲区溢出等安全问题。 适用于系统级编程 C++ 手动内存管理:C++需要程序员手动管理内存,包括分配和释放内存,这可能导致内存泄漏和指针错误。 适用于游戏引擎和系统级编

    2024年02月08日
    浏览(57)
  • 【C++】多态原理剖析,Visual Studio开发人员工具使用查看类结构cl /d1 reportSingleClassLayout

    author:Carlton tag:C++ topic:【C++】多态原理剖析,Visual Studio开发人员工具使用查看类结构cl /d1 reportSingleClassLayout website:黑马程序员C++ tool:Visual Studio 2019 date:2023年7月24日   目录 父类使用虚函数前后类内部结构变化 子类重写父类虚函数的作用及其机理         首先父类成员

    2024年02月15日
    浏览(40)
  • C语言指针(适合C语言进阶者):一道题带你深入理解数组与指针的关系

    🎈个人主页:JAMES别扣了 💕在校大学生一枚。对IT有着极其浓厚的兴趣 ✨系列专栏目前为C语言初阶、后续会更新c语言的学习方法以及c题目分享. 😍希望我的文章对大家有着不一样的帮助,欢迎大家关注我,我也会回关,大家一起交流一起互动,感谢大家的多多支持哈! 🎉

    2024年04月16日
    浏览(38)
  • C++语言程序设计之类和对象进阶(3)

            这一部分介绍C++友元函数、友元类和this指针。         友元函数,可以在类的成员函数外部直接访问对象的私有成员。 1.1.1 设计代码 1.1.2 执行结果 图1 友元函数代码执行结果

    2024年01月25日
    浏览(26)
  • 【C++ 进阶】第 1 章:[C 语言基础] C 语言概述与数据类型

    目录 一、C 语言的概述  (1)计算机结构组成 (2)计算机系统组成  (3)ASCII 码 (4)计算机中的数制及其转换 (5)程序与指令  (6)语言的层次划分 (7)主流语言进化史 (8)IDE - VisualStudio 2022 简介 (9)C 语言是面向过程的编程语言 (10)思维导图 (11)详细知识汇

    2024年02月15日
    浏览(30)
  • Java 封装 继承 多态(深入理解)

    登神长阶 第二阶 封装 继承 多态 🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀 目录 🍒一.面向对象编程的三大特性 🍍二.封装 🧉1.定义及其作用  🥝2.访问限定符 🫛3.封装扩展 包(package) 🥕3.1.定义及其作用  🥦 3.2.导入包的类 🍔3.3.自定义包 🌯

    2024年03月11日
    浏览(81)
  • 如何理解Java中的多态?

    多态的概念比较简单,就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。 如果按照这个概念来定义的话,那么多态应该是一种运行期的状态。为了实现运行期的多态,或者说是动态绑定,需要满足三个条件: 有类继承或者接口实现。 子类要重写父

    2024年02月14日
    浏览(26)
  • Java中的多态如何理解——详解

    🎈🎈🎈本篇文章我们主要讲解的是Java中的多态,那么什么是多态呢? 同类型的对象,执行同一个行为,会表现出不同的行为特征。 接下来让我们一起对多态进行详细地讲解。   多态的常见形式: 父类类型 对象名称 = new 子类构造器; 接口 对象名称 = new 实现类构造器;

    2023年04月12日
    浏览(27)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包