C++之类继承和多态(虚函数、构造函数,析构函数)

C++(多态,虚函数,构造函数,static) C++重要知识点(虚函数,拷贝构造函数,static) 阅读详情

1.虚函数

C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

#include<iostream>
#include<string>
using namespace std;
class Flower
{
public:
	virtual void display()//基类的虚函数,形成动态绑定,永远不会运行“动物”,Flower类也从1个字节变成了4个字节(指针)
		//只有在程序运行的时候才知道speak函数具体运行哪个子类的speak函数
	{
		cout << "动物!" << endl;
	}
};

class Rose:public Flower
{
public:	
	void display()//派生类重写了基类的虚函数,覆盖了原来虚函数表中的地址
	{
		cout << "玫瑰" << endl;
	}
};

void fun(Flower &f1)//派生类的对象作为实参,形参可以用基类的引用或者指针来接收,目的在于实现多态性
{//实现了派生类对象向基类类型的转换
	f1.display();//加上virtual之后,这一行就要去看你f1派生对象的类型,去调用相对应的display函数
}

int main() {
	Rose r1;
	fun(r1);
	system("pause");
	return 0;
}

下面通过编译器开发人员命令提示来说明多态的问题,当在基类没有virtual时,这时基类实际上是一个空类,只占1个字节。

 当加上virtual之后,基类变成了4个字节

 多了一个vfptr:virtual function pointer虚函数指针(指针4个字节),指向一个虚函数表vftable。

到了派生类,派生类如果没有重写虚函数,此时占4个字节,为从基类继承而来的vfptr,指向了哪个基类的vftable,这个是完完全全从基类继承过来的吧,如下图:

 所以,当你在调用函数调用display的时候,指向的是Flower::display,但本意是要他输出rose的,所以在派生类重写了虚函数之后,再看:

 这样,再调用的时候,就会调用Rose作用域下的display函数了。

由于基类中的虚函数一般不会被执行,那么就直接

virtual void display() = 0;

此时,这个虚函数被称为纯虚函数,这个Flower类就被称为抽象类

当是抽象类的时候,不能实例化对象,包括堆区和栈区,但可以定义一个类指针,比如:

Flower f1;//报错

new Flower;//报错

当一个抽象类作为派生类的父类时,派生类必须要重写这个纯虚函数,否则这个类也将会被定义为抽象类,不能被实例化

这个目的就是要实现多态,重写虚函数

通过一个父类指针,只想多个子类的对象,加强通用性。

 2.虚析构和纯虚析构

 先放上一段代码,不涉及虚析构和纯虚析构,只是我们要再此基础上添内容。

#include<iostream>
#include<string>
using namespace std;

class Flower
{
public:
	Flower()
	{
		cout << "Flower的构造函数" << endl;
	}
	virtual void display() = 0;
	~Flower()
	{
		cout << "Flower的析构函数" << endl;
	 }
};

class Rose:public Flower
{
public:	
	Rose()
	{
		cout << "Rose的构造函数" << endl;
	}
	void display()
	{
		cout << "玫瑰" << endl;
	}
	~Rose()
	{
		cout << "Rose的析构函数" << endl;
	}
};
void fun()
{
	Rose r1;
	Flower* f1 = &r1;
	f1->display();
}
int main() {
	fun();
	system("pause");
	return 0;
}

运行结果:

比如说,派生类中有在堆区开辟的一段空间,那么按道理说,应该在派生类的析构函数中去释放,这个需要注意。

但还有一种情况,在调用函数中基类的指针或引用绑定的是堆区的派生类数据。如下所示:

 

#include<iostream>
#include<string>
using namespace std;

class Flower
{
public:
	Flower()
	{
		cout << "Flower的构造函数" << endl;
	}
	virtual void display() = 0;
	~Flower()
	{
		cout << "Flower的析构函数" << endl;
	}
};

class Rose :public Flower
{
public:
	Rose(string c)
	{
		color = c;
		cout << "Rose的构造函数" << endl;
	}
	void display()
	{
		cout << "玫瑰" << endl;
	}
	~Rose()
	{
		cout << "Rose的析构函数" << endl;
	}
	string color;
};

void fun()
{
	Flower* f1 = new Rose("blue");//在局部函数定义的f1,利用完就要释放
	f1->display();}

int main() {
	fun();
	system("pause");
	return 0;
}

 如果你f1不释放,这时候,造成程序内存泄露,两个类始终都不会调用析构函数,直到程序结束。

进一步的,我如果注意到了f1要释放,因此在fun函数中加了一句

delete f1;

这时,我们认为至少是调用函数中的f1内存被释放了,但是,问题在于当调用函数结束后,Rose中的析构函数并没有被执行,原因通过父类指针去释放,会导致子类对象可能清理不干净,即一旦子类对象中有在堆区开辟的数据,在析构中去释放,但这种情况下根本不会去走派生类的析构函数,造成内存泄漏。如下图所示,根本没有Rose的析构:

 

问题总结:当基类指针指向或引用堆区开辟的派生类时,删除父类指针并不会调用子类的析构函数

派生类除了自己的析构函数,还要继承基类的析构函数,用父类的指针默认调用的时父类的析构

要想解决,就要将父类的析构函数加上virtual变成虚析构,这样的话,在析构的时候才会先调用子类的析构,再调用父类的析构,完整程序如下:

#include<iostream>
#include<string>
using namespace std;
class Flower
{
public:
	Flower()
	{
		cout << "Flower的构造函数" << endl;
	}
	virtual void display() = 0;
	virtual ~Flower()
	{
		cout << "Flower的析构函数" << endl;
	}
};
class Rose :public Flower
{
public:
	Rose(string c)
	{
		color = c;
		cout << "Rose的构造函数" << endl;
	}
	void display()
	{
		cout << "玫瑰" << endl;
	}
	~Rose()
	{
		cout << "Rose的析构函数" << endl;
	}
	string color;
};
void fun()
{
	Flower* f1 = new Rose("blue");//在局部函数定义的f1,利用完就要释放
	f1->display();
	delete f1;
}
int main() {
	fun();
	system("pause");
	return 0;
}

 纯虚析构其实比较鸡肋,在析构函数的前面加上virtual,形成纯虚析构,这时候这个类也会被定义为抽象类。但要注意的是,纯虚析构虽然是后边等于0,但是结构上一定要有具体的实现,否则编译就会被报错。

 3.构造函数[下篇会专门推送构造函数]

由上述程序可以看到:
派生类构造时,先构造基类部分,然后再构造派生类部分;派生类析构时,先析构派生类部分,然后再析构基类部分。
因此:在基类构造函数执行的时候,派生类的部分是未定义状态;在基类析构函数执行的时候,派生类的部分已经被释放了。
所以在基类的构造函数或析构函数中调用虚函数是不建议的

 

C++继承和多态总结 继承 继承:在已有类的基础上创建新类的过程 一个 B 类继承A类,或称从类 A 派生类 B 类 A 称为基类(父类),类 B 称为派生类(子类) 多继承方式下构造函数的执行顺序: 先执行所有基类的构造函数 在C++的继承机制中,派生类吸收基类中除构造函数和析构函数之外的全部成员。 再执行对象成员的构造函数 通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员。 最后执行派生类的构造函数 仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身 阅读详情

相关推荐

大数据运维面试题及答案

1. 架构组成:- 2 个 NameNode(Active 活跃节点、Standby 备用节点),实时同步元数据(通过 JournalNode 集群,默认 3 个节点,存储 edits 日志,Active 写入、Standby 读取以保持元数据一致);- 容错性强:通过 RDD 的 “ lineage(血缘)” 机制,任务失败时可快速重算,无需从头执行。:集群 “大脑”,管理元数据(文件目录、块位置),实时响应客户端请求,数据存于内存,元数据镜像(fsimage)和编辑日志(edits)持久化到磁盘;

weixin_42795092的博客 907

C++核心——多态里的构造和析构函数

C++核心——多态里的构造和析构函数 一、构造函数和析构函数 对象的初始化和清理也是两个非常重要的安全问题 ​ 一个对象或者变量没有初始状态,对其使用后果是未知 ​ 同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题 c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。 对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不...

wtzhu_13的博客 5705

ggml_cgraph 详解

是 GGML 库的(Computation Graph)结构,它是深度学习框架的核心,用于表示、优化和执行神经网络的计算过程。

zhouzwd87的专栏 1203

C++构造函数、虚函数

一:构造函数 基本构造函数: 执行过程:1)传参 2)给类数据成员开辟空间 3)执行冒号语法给数据成员初始化 4)执行构造函数括号里面的内容 例如: class Base{ public: # 冒号语法后面的内容相当于int a = 10;(初始化) Base(int var) : m_Var(var){ } private: int m_...

qq26983255的博客 1971

【AGC】【serverless】【云函数】云函数常见问题

1、云函数添加了AUTH触发器后,在用户执行注册、销户、登录、登出动作时,云函数的event对象会自动触发返回相应信息。比如我在“层”的压缩包里放了config/config.txt,那么我在云函数里怎么调用这个文件?层的目录是:/dcache/layer,函数的目录是/dcache/layer/func。在云函数里往云数据库里插入数据返回的是180000,这是正常吗?已经按照文档在后台对应的云函数里配置了,所需的AUTH触发器。层的用法和放在函数里的用法是相同的。云函数里的层如何调用?

华为开发者联盟 839

SuperPoint+SuperGlue在HLoc中的实战效果对比:5大公开数据集测试报告

本文在HLoc框架下,对传统SIFT与深度学习组合SuperPoint+SuperGlue在五大公开数据集上的视觉定位效果进行了实战对比测试。报告详细分析了两种方案在Aachen Day-Night、RobotCar Seasons等不同光照、季节及场景下的表现,揭示了SuperPoint+SuperGlue在应对剧烈光照变化和长期外观变化时的显著优势,并提供了基于HLoc模块化设计的选型与配置策略。

weixin_29093169的博客 425

6.15C++:虚函数、构造函数不可以为虚函数、虚析构函数、虚函数实现多态、虚函数中默认参数、基类虚函数不能是私有函数、虚函数对象调用可内联,指针/引用调用不能内联

1 虚函数 虚函数可以实现运行时的多态,在运行时觉得调用哪个函数体; 虚函数必须是非静态的成员函数,虚函数是属于对象的,不是属于整个类的,需要在运行时通过指针定位到指向的对象,然后决定调用哪个函数体; 虚函数经过派生之后,就可以实现运行过程中的多态; 构造函数不能是虚函数,析构函数可以; 虚函数virtual只能出现在类定义的函数原型声明中,而不能在成员函数实现的时候; 虚函数不能是内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的 #include <iostream>

weixin_44478659的博客 632

从宁德新能源面试官视角,拆解Halcon/OpenCV图像处理工程师的硬核技能树(附避坑指南)

本文从宁德新能源面试官视角出发,详细拆解Halcon/OpenCV图像处理工程师的核心技能树,涵盖硬件选型、算法实现和系统工程思维三大能力维度。文章提供工业视觉工程师面试的实战避坑指南,包括硬件配置要点、算法优化技巧及职业发展路径,助力求职者系统提升图像处理专业技能。

weixin_28732953的博客 129

C++基础(九)C++的构造函数:构造函数的多态

一、简介 主要参考:http://c.biancheng.net/view/149.html 二、构造函数的多态 #include "stdafx.h" #include&lt;iostream&gt; #include&lt;string&gt; using namespace std; class Complex { private: double real, imag; publ...

xpj8888的博客 1474

音频分割:长语音音频 分割为 短语音音频 - python 实现

音频分割:长语音音频 分割为 短语音音频 - python 实现

DATABALL 的博客 1207

网络速度的极致:800G & 1.6T 以太网的突破!

你好,这里是网络技术联盟站。在我们进入数据驱动的世界时,以太网的重要性日益突出。从根本上说,以太网是一种连接计算机的技术,旨在形成一个本地网,通过它,设备可以与其他设备进行通信。然而,随着时间的推移,以太网已经发展成为一种全球的数据通信系统,其速度从初始的10Mbit/s发展到现在的800G,甚至1.6T。这种巨大的进步并不是没有挑战的,但每一次的突破都代表了技术的巨大飞跃。本文将探讨800G和1.6T以太网的创新与挑战。

网络技术联盟站 1771

C++ 构造函数和析构函数可以是虚函数嘛?

简单总结就是:构造函数不可以是虚函数,而析构函数可以且常常是虚函数。 构造函数不能是虚函数 1. 从vptr角度解释 虚函数的调用是通过虚函数表来查找的,而虚函数表由类的实例化对象的vptr指针(vptr可以参考C++的虚函数表指针vptr)指向,该指针存放在对象的内部空间中,需要调用构造函数完成初始化。如果构造函数是虚函数,那么调用构造函数就需要去找vptr,但此时vptr还...

Magnum的博客 1万+

华为OD机试真题2025双机位A卷 Python&JavaScript 实现【采购订单】

在一个采购系统中,采购申请(PR)需要经过审批后才能生成采购订单(PO)。每个PR包含商品的单价(假设相同商品的单价一定是一样的)及数量信息。系统要求对商品进行分类处理:单价高于100元的商品需要单独处理,单价低于或等于100元的相同商品可以合并到同一采购订单PO中。针对单价低于100的小额订单,如果量大可以打折购买。

MISAYAONE的博客 1459

C++ 构造函数和析构函数是否可以为虚函数?

简单总结就是:构造函数不可以是虚函数,而析构函数可以且常常是虚函数。 理由如下: 构造函数不能是虚函数 1.从vptr角度解释 虚函数的调用是通过虚函数表来查找的,而虚函数表由类的实例化对象的vptr指针(vptr可以参考C++的虚函数表指针vptr)指向,该指针存放在对象的内部空间中,需要调用构造函数完成初始化。如果构造函数是虚函数,那么调用构造函数就需要去找vptr,但此时vptr还没有初始化! 2.从多态角度解释 虚函数主要是实现多态,在运行时才可以明确调用对象,根据传入的对象类型来调用函数,例如通过

TABE_的博客 7841

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【C++】继承、虚函数、多态

详细介绍C++的继承、虚函数、多态以及虚函数表,并通过代码示例说明

weixin_40514381的博客 1459

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

构造函数和析构函数可以是虚函数吗?为什么?

三、构造函数和析构函数可以是虚函数吗?为什么? 1、构造函数 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有

Yy的博客 5459

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

C++ 构造函数和析构函数中调用虚函数的问题

C++构造函数和析构函数中调用虚函数效果与原因分析。

noevil的博客 1540

构造函数、析构函数、多态、虚函数、纯虚函数、虚析构函数、拷贝构造函数、protected、private、public

用来实现多态,允许基类的函数被实例化。通过在基类中声明虚函数,并在派生类中进行重写,实现了在运行时根据对象的实际类型选择合适的函数实现。

Ceri的博客 1827

C++ 多态与虚函数、与构造函数和析构函数的联系

多态与虚函数 面向对象编程中,多态的含义是“一个接口,多种实现”。 多态分为静态多态和动态多态。静态多态是通过模板化和重载技术来实现,在编译的时候确定。动态多态通过虚函数和继承关系来实现,执行动态绑定,在运行的时候确定。 C++中运行时的多态是指根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。 C++多态性是通过虚函数来实现的,虚...

a834595603的博客 991

C++:构造函数和析构函数可以是虚函数吗

先说结论:C++中,构造函数不可以是虚函数,而析构函数可以且常常是虚函数。 虚函数的定义:类成员函数前面添加virtual关键字,则函数被称为虚函数。 1、构造函数不可以是虚函数 当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个存储成员函数指针的数据结构。 虚函数表是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中,当存在虚函数时,每个对象都有一个指向虚函数的指针(vptr指针)。在实现多态的过程中,父类和派生类都有vptr指针。 vptr的初始化:当对象

Taopper的博客 3426

C++基础| 多态 与 虚函数 大汇总:C++怎么实现多态 、虚函数和纯虚函数的区别 、虚函数是怎么实现的 、虚函数表是什么 、什么是构造函数和析构函数?构造函数、析构函数可以是虚函数吗

C++基础| 多态 与 虚函数 大汇总:C++怎么实现多态 、虚函数和纯虚函数的区别 、虚函数是怎么实现的 、虚函数表是什么 、什么是构造函数和析构函数?构造函数、析构函数可以是虚函数吗

2402_84438596的博客 1072

【c++】多态中的构造函数和析构函数

ps:构建派生类对象时首先构造匿名的基类对象(前面提到过)此时调用基类的构造函数,使得虚函数指针指向基类的虚函数表,接着调用派生类的构造函数,重置虚表指针指向派生类的虚表。会触发动态绑定,正确调用。的顺序进行,vptr 的。:将基类析构函数声明为。(编译器隐式处理)。

2201_75930743的博客 1054

为什么在构造函数和析构函数中调用虚函数不会发生多态?请从vptr的初始化时机解释

构造/析构函数中调用虚函数无多态,本质是vptr的指向与构造/析构顺序强绑定构造时:vptr先指向基类vtable(基类构造阶段),后升级为派生类vtable(派生类构造阶段)。基类构造内无法访问派生类vtable,派生类构造内调用自身虚函数也非多态。析构时:vptr先保持派生类vtable(派生类析构阶段),后降级为基类vtable(基类析构阶段)。基类析构内无法访问派生类vtable,因此无多态。简言之,

C/C++ 1013

C++ 构造函数不能是虚函数,基类析构函数应该为虚函数

文章目录1.构造函数不能是虚函数二、析构函数可以且常常是虚函数 简单总结就是:构造函数不可以是虚函数,而析构函数可以且常常是虚函数。 1.构造函数不能是虚函数 从vptr角度解释   虚函数的调用是通过虚函数表来查找的,而虚函数表由类的实例化对象的vptr指针(vptr可以参考C++的虚函数表指针vptr)指向,该指针存放在对象的内部空间中,需要调用构造函数完成初始化。如果构造函数是虚函数,那么调用构造函数就需要去找vptr,但此时vptr还没有初始化!    虚函数对应一个虚函数表vtbl,可是这个

森明帮大于黑虎帮的博客 1116

C++语法——多态,虚函数,纯虚函数,虚函数表,虚继承

包含纯虚函数的类称为抽象类(Abstract Class)。抽象类仅仅提供了一些接口,但是没有实现具体的功能。作用就是制定各种接口,通过派生类来实现不同的功能,从而实现代码的复用和可扩展性。

a45467911的博客 996

C++面向对象之类的继承与派生、虚继承、虚函数、多态性、纯虚函数、抽象类、虚析构函数

实际上继承和派生是一个意思,只是说站在不同的角度来看而已。基类派生出派生类,派生类继承自基类。 继承的方式 首先已经定义了一个基类BaseClass,再定义一个类ChildClass,继承自这个基类,格式如下: class ChildClass: [继承方式] BaseClass{ //子类新增的数据成员和成员函数 }; 继承方式有public 、private、protected ,如果不写,默认为private 在派生类中,能够使用的只有public和protected修饰的成员。private成员

Cser_zhu的博客 700
上一篇: C++之运算符重载
下一篇: C++之函数模板
Worthwhile_HUST
博客等级 码龄5年 53粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值