IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++动态多态的简单讨论 -> 正文阅读

[C++知识库]C++动态多态的简单讨论

一、基本约定

  1. 如果你以一个"基类之指针"指向一个"派生类之对象",那么经由该指针你只能调用该基类所定义的函数。
  2. 如果你以一个“派生类之指针”指向一个“基类之对象”,你必须先做明显的转型操作(explicit cast),这种作法很危险。
  3. 如果基类和派生类都定义了“相同名称之函数”,那么通过对象指针调用成员函数时,到底调用了那个函数,必须视该指针的原始类型而定,而不是视指针实际所指的对象的类型而定。1

1.1 基类指针引用派生类对象

?? 用基类指针引用一个派生类对象,由于派生类对象也是基类的对象,所以这种引用是安全的。但是只能引用基类成员。若试图通过基类指针引用那些只在派生类中才有的成员,编译器会报告语法错误2。如下例所示:

//Parent.h
#pragma once

class CParent{
public:
	int Parent_var_a;
	CParent();
	~CParent();
};

//Subclasses.h
#pragma once
#include "Parent.h"

class CSubclasses :	public CParent{
public:
	int Subclasses_var_a;
	CSubclasses();
	~CSubclasses();
};

//main.cpp
#include "stdafx.h"
#include "iostream"
#include "Parent.h"
#include "Subclasses.h"

using namespace std;

int main(){
	CParent* p_parent = new CSubclasses();
	p_parent->Parent_var_a = 0;
	/***********************************************************/
	//通过基类指针引用那些只在派生类中才有的成员,编译器会报告语法错误。
	p_parent->Subclasses_var_a = 0;
	/***********************************************************/
	cout << p_parent->Parent_var_a << endl;
	cout << p_parent->Subclasses_var_a << endl;
    return 0;
}

图 1-1 基类指针引用派生类中成员

图 1-1 基类指针引用派生类中成员

1.2 派生类指针引用基类对象

?? 这种引用方式会导致语法错误,派生类指针必须先强制转换为基类指针,这种方法是不安全的。因此本文不再过多讨论。

二、动态多态

?? 动态多态(动态绑定):即运行时的多态,在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。动态多态实现的关键是通过子类重写父类的虚函数实现的。需要有继承关系,并且在子类中重写父类中的虚函数。使用的关键是父类指针或引用指向子类对象3

2.1 虚函数

2.1.1 虚函数的定义及使用

虚函数是在父类中使用关键字 virtual声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定4

?? 简单来说父类指针或引用指向子类对象,并调用父、子类中的同名函数时,到底会执行哪个函数是问题的关键,如下例:

#include "stdafx.h"
#include "iostream"

using namespace std;

class CParent {
public:
	CParent(){}
	~CParent(){}
	void function() {
		cout << "Parent function" << endl;
	}
};

class CSubclasses : public CParent {
public:
	CSubclasses(){}
	~CSubclasses(){}
	void function() {
		cout << "Subclasses function" << endl;
	}
};

int main(){
	CParent* p_parent = new CSubclasses();
	p_parent->function();
    return 0;
}

图 2-1 未使用虚函数声明时的调用情况

图 2-1 未使用虚函数声明时的调用情况

?? 调用函数 function() 被编译器设置为父类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 function() 函数在程序编译期间就已经设置好了。
?? 若如果想调用子类中的函数,需要在父 类中,function() 的声明前放置关键字 virtual,如下所示:

class CParent {
public:
	CParent(){}
	~CParent(){}
	//void function(){
	//虚函数标识
	virtual void function() {
		cout << "Parent function" << endl;
	}
};
....

图 2-2 使用虚函数声明时的调用情况

图 2-2 使用虚函数声明时的调用情况

?? 此时,编译器看的是指针的内容,而不是它的类型。子类拥有自己的函数function() 的独立实现。这就是多态的一般使用方式。有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。

2.1.2 同名函数但参数不同时的调用情况

?? 同样用父类指针或引用指向子类对象,并调用基、子类中的同名函数时,但此时我们稍作一个修改,将基类和子类中的函数参数表现的有所差异。

#include "stdafx.h"
#include "iostream"

using namespace std;

class CParent {
public:
	CParent(){}
	~CParent(){}
	void function(int var_a) {
		cout << "Parent function" << endl;
	}
};

class CSubclasses : public CParent {
public:
	CSubclasses(){}
	~CSubclasses(){}
	void function() {
		cout << "Subclasses function" << endl;
	}
};

int main(){
	CParent* p_parent = new CSubclasses();
	p_parent->function();
    return 0;
}

?? 其结果如下,可以更为明显的看出调用函数 function() 被编译器设置为父类中的版本。
图 2-3 同名函数但参数不同时的调用情况

图 2-3 同名函数但参数不同时的调用情况

2.2 实例化对象调用同名函数

2.2.1 实例化对象调用同名函数

?? 若此时不用父类指针或引用指向子类对象,而是各自生成实例化对象,来调用同名函数。

#include "stdafx.h"
#include "iostream"

using namespace std;

class CParent {
public:
	CParent(){}
	~CParent(){}
	void function() {
		cout << "Parent function" << endl;
	}
};

class CSubclasses : public CParent {
public:
	CSubclasses(){}
	~CSubclasses(){}
	void function() {
		cout << "Subclasses function" << endl;
	}
};

int main(){
	CParent m_parent;
	CSubclasses m_subclasses;
	m_parent.function();
	m_subclasses.function();
    return 0;
}

?? 其结果如下,可以看出实例化对象调用的都是其自身的函数,其原理也是显而易见。
图 2-4 实例化对象调用同名函数

图 2-4 实例化对象调用同名函数

2.2.2 各自指针调用同名函数

?? 进一步说,若通过各自的指针调用同名函数时,到底会执行哪个函数,如下例:

#include "stdafx.h"
#include "iostream"

using namespace std;

class CParent {
public:
	CParent(){}
	~CParent(){}
	void function() {
		cout << "Parent function" << endl;
	}
};

class CSubclasses : public CParent {
public:
	CSubclasses(){}
	~CSubclasses(){}
	void function() {
		cout << "Subclasses function" << endl;
	}
};

int main(){
	CParent* p_parent = new CParent();
	CSubclasses* p_subclasses = new CSubclasses();
	p_parent->function();
	p_subclasses->function();
    return 0;
}

?? 其结果如下,可以看出调用的也都是其自身的函数。
图 2-5 各自指针调用同名函数

图 2-5 各自指针调用同名函数

三、纯虚函数

?? 在对接口进行定义进行抽象类编写时,在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。纯虚函数的声明格式如下:

virtual 返回值类型 函数名 (函数参数) = 0;

?? 例如:

#pragma once
#include "Parent.h"
class CSubclasses :	public CParent{
public:
	int Subclasses_var_a;
	CSubclasses();
	~CSubclasses();
	//纯虚函数定义
	virtual int parent_function() = 0;
};

?? 当然带有参数的纯虚函数声明也是合法的,例如:

#pragma once
#include "Parent.h"
class CSubclasses :	public CParent{
public:
	int Subclasses_var_a;
	CSubclasses();
	~CSubclasses();

	virtual int parent_function(int var_a,int var_b) = 0;
};

四、小结

?? 对C++动态多态的进行了简单的讨论。通过基本约定,明确了只能基类指针引用派生类对象。对动态多态的定义进行了描述。说明了虚函数的定义及基本使用方法。并进一步对同名函数但参数不同时的调用情况、实例化对象调用同名函数情况、各自指针调用同名函数情况,三种情况进行了简单讨论。

五、参考文献

[1] 侯捷.深入浅出MFC[M].武汉:华中科技大学出版社,2001:53.
[2] https://www.cnblogs.com/iois/p/4537736.html
[3] https://blog.csdn.net/qq_21989927/article/details/111226696
[4] https://www.runoob.com/cplusplus/cpp-polymorphism.html


  1. 侯捷.深入浅出MFC[M].武汉:华中科技大学出版社,2001:53. ??

  2. https://www.cnblogs.com/iois/p/4537736.html ??

  3. https://blog.csdn.net/qq_21989927/article/details/111226696 ??

  4. https://www.runoob.com/cplusplus/cpp-polymorphism.html ??

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 11:59:07  更:2021-08-11 12:06:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:19:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码