当前位置: 首页 > article >正文

C++ 多态相关

文章目录

  • 1. 静态类型和动态类型
    • 1.1 静态类型:对象定义时的类型,编译期间就确定好的。
    • 1.2 动态类型:对象目前所指向的类型(运行的时候才决定类型的)。
  • 2. 静态绑定和动态绑定
  • 3. 继承的非虚函数坑
  • 4. 虚函数的动态绑定
  • 5. 重新定义虚函数缺省参数(坑)
  • 6 C++ 中的多态性

1. 静态类型和动态类型

1.1 静态类型:对象定义时的类型,编译期间就确定好的。

静态类型是指在编译时就能确定的数据类型或变量的类型
如下代码

#include <iostream>

class Base
{};

class Derive : public Base
{};

class Derive2 : public Base
{};

class Derive3 : public Base
{};

int main()
{
	Base base;						//静态类型是 Base
	Derive derive;					//静态类型是 Derive
	Base* pbase = nullptr;;			//无论指向父类还是子类,定义时类型为 Base*,静态类型Base*
	Base* pbase2 = new Derive2();	//静态类型依然是 Base*
	Base* pbase3 = new Derive3();	//静态类型依然是 Base*
}

Base base; //静态类型是 Base
Derive derive; //静态类型是 Derive
Base* pbase = nullptr ; // 无论指向父类还是子类,定义时类型为 Base* ,静态类型是Base*
Base* pbase2 = new Derive2(); //静态类型依然是 Base*
Base* pbase3 = new Derive3(); //静态类型依然是 Base*

1.2 动态类型:对象目前所指向的类型(运行的时候才决定类型的)。

一般来讲,只有指针或者引用才有动态类型的说法,而且一般都是指父类的指针或者引用。
动态类型描述指针所指向的对象的实际类型
动态类型描述引用,指的是该引用当前所绑定对象的实际类型。

代码如下

#include <iostream>

class Base
{};

class Derive : public Base
{};

class Derive2 : public Base
{};

class Derive3 : public Base
{};

int main()
{
	Base base;						//静态类型是 Base
	Derive derive;					//静态类型是 Derive
	Base* pbase = nullptr;;			// 无论指向父类还是子类定义时 Base* ,静态类型是Base*
	Base* pbase2 = new Derive2();	//静态类型依然是 Base*
	Base* pbase3 = new Derive3();	//静态类型依然是 Base*
}
  • base 和 derive 没有动态类型,因为他们既不是指针也不是引用
  • pbase 也没有动态类型,因为他没有指向任何对象.
  • pbase2 有动态类型,指向的对象的动态类型是 Derive2.
  • pbase3 有动态类型,指向的对象的动态类型是 Derive3.

动态类型在执行过程中可以改变,如下
pbase = pbase2; //指向的对象的动态类型是 Derive2
pbase = pbase3; //指向的对象的动态类型是 Derive3

2. 静态绑定和动态绑定

静态绑定: 绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期.
动态绑定: 绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期.

  1. 普通成员函数是静态绑定,而虚函数是动态绑定。
  2. 缺省参数一般是静态绑定

3. 继承的非虚函数坑

代码如下

#include <iostream>

class Base
{
public:
	void myfunc()
	{
		std::cout << "Base::myfunc()" << std::endl;
	}
};

class Derive : public Base
{
public:
	void myfunc()
	{
		std::cout << "Derive::myfunc()" << std::endl;
	}
};

int main()
{
	Derive derive;
	Derive* pderive = &derive;
	pderive->myfunc();					//Derive::myfunc()

	Base* pbase = &derive;
	pbase->myfunc();					//Base::myfunc()

	return 0;
}

myfunc

可得:
普通成员函数是静态绑定, 执行myfunc()取决于调用者的静态类型. pderive的静态类型是Derive*,所以调用的是Derive的myfunc(), pbase的静态类型是Base*,调用的是Base的myfunc()

4. 虚函数的动态绑定

代码如下

#include <iostream>

class Base
{
public:

	virtual void myvirfunc()
	{
		std::cout << "Base::myvirfunc()" << std::endl;
	}
};

class Derive : public Base
{
public:
	virtual void myvirfunc()
	{
		std::cout << "Derive::myvirfunc()" << std::endl;
	}

};

int main()
{
	Derive derive;
	Base   base;

	Derive* pderive = &derive;
	Base* pbase = &derive;

	pderive->myvirfunc();					//Derive::myvirfunc()
	pbase->myvirfunc();						//Derive::myvirfunc()

	pbase = &base;
	pbase->myvirfunc();						//Base::myvirfunc()

	return 0;
}

virtual
可得:
虚函数是动态绑定,pbase指向的对象的动态类型开始是 Derive 调用的是Derive的myvirfunc()
后来pbase指向的对象的动态类型变为Base,调用的是Base的myvirfunc()。

5. 重新定义虚函数缺省参数(坑)

代码如下

#include <iostream>

class Base
{
public:

	virtual void myvirfunc(int value = 1)
	{
		std::cout << "Base::myvirfunc(), value = " << value << std::endl;
	}
};

class Derive : public Base
{
public:
	virtual void myvirfunc(int value = 2)
	{
		std::cout << "Derive::myvirfunc(), value = " << value << std::endl;
	}

};

int main()
{
	Derive derive;
	Base   base;

	Derive* pderive = &derive;
	Base* pbase = &derive;

	pderive->myvirfunc();					//Derive::myvirfunc()
	pbase->myvirfunc();						//Derive::myvirfunc()

	pbase = &base;
	pbase->myvirfunc();						//Base::myvirfunc()

	return 0;
}

default

Derive derive;
Base* pbase = &derive;
pbase->myvirfunc(); //Derive::myvirfunc()

defaultt

defaultt
观察:
执行子类的虚函数,但输出形参value的值确实是父类虚函数形参的缺省值
可得:
缺省参数是静态绑定的(pbase的静态类型是Base*) 绑定到Base对应myvirfunc()的缺省参数上。
注意:
不要在子类中重新定义虚函数缺省参数的值

6 C++ 中的多态性

多态必须存在虚函数并且调用虚函数,没有虚函数,多态不存在

  1. 从代码实现上
  • 当调用虚函数时,调用路线是通过查询虚函数表找到虚函数入口然后调用
  1. 从表现形式上看
  • 父类子类存在继承关系,父类中含有虚函数子类重写父类中的虚函数
  • 父类指针(引用)指向子类对象
  • 父类的指针(引用)调用子类重写的虚函数

http://www.kler.cn/a/402342.html

相关文章:

  • 《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试
  • GitLab|应用部署
  • python(四)os模块、sys模块
  • TCL大数据面试题及参考答案
  • 机器学习与图像处理中上采样与下采样
  • java基础概念36:正则表达式1
  • 【专题】中国企业出海洞察报告暨解码全球制胜之道报告汇总PDF洞察(附原数据表)
  • 小程序19-微信小程序的样式和组件介绍
  • React——useCallback
  • Kubernetes常用命令
  • 2025年软考报名时是什么时候?开考科目如何安排?
  • 使用ufw配置防火墙,允许特定范围IP访问
  • 解决 electron 打包后部分电脑报错 Error: Dynamic Symbol Retrieval Error: Win32 error 126
  • CI配置项,IT服务的关键要素
  • Vue3 + Vite 项目引入 Typescript
  • 应聘美容师要注意什么?博弈美业收银系统/管理系统/拓客系统分享建议
  • 【并发模式】Go 常见并发模式实现Runner、Pool、Work
  • 海外媒体软文发稿:打开全球传播的新窗口-大舍传媒
  • Android CCodec Codec2 (二一)InputBuffers
  • 【工控】线扫相机小结 第三篇
  • 项目进度计划表:详细的甘特图的制作步骤
  • Vulnhub靶场案例渗透[11]- Momentum2
  • Linux进阶:压缩、解压
  • 开源控件:Qt/C++自定义异形窗口和颜色选择器 【工程源码联系博主索要】
  • 【游戏开发】【Unity】基本的Unity概念
  • 深入解析 MySQL 数据库:负载均衡