突破编程_C++_面试(基础知识(11))

面试题34:什么是继承,它有哪些类型

继承是面向对象编程的一个基本概念,它允许一个类(派生类、子类)继承另一个类(基类、父类)的属性和方法。继承可以减少代码冗余,提高代码重用性,并且有助于创建更复杂的类结构。
要在派生类中继承基类,只需在派生类定义的时候列出基类的名称,并指定继承方式(公有、保护或私有):

class BaseClass 
{  // 基类的成员  
};  class DerivedClass : public BaseClass 
{  // 派生类的成员  
};

在上面代码中,Derived 类公有地继承了 Base 类。这意味着 Base 类中的公有成员和保护成员在 Derived 类中保持原有的访问权限(公有和保护),而 Base 类中的私有成员在 Derived 类中是不可访问的。
派生类是基类的一个特殊化版本。这意味着派生类拥有基类所有的属性和方法,同时还可以定义自己的新属性和方法。这种关系通常被称为 “IS-A” 关系,即派生类 “是一种” 基类。
C++支持三种继承方式:
(1)公有继承( public ):
在公有继承中,基类中的公有成员在派生类中保持公有,基类的保护成员在派生类中保持保护,而基类的私有成员在派生类中是不可访问的。这是最常见的继承方式。
(2)私有继承( private ):
在私有继承中,基类中的所有成员(公有、保护和私有)在派生类中都是私有的。这意味着派生类的对象不能访问基类中的公有和保护成员。私有继承通常用于实现接口继承,即派生类仅继承基类的接口而不继承其实现。
(3)保护继承( protected ):
在保护继承中,基类中的公有成员和保护成员在派生类中都是保护的,而基类的私有成员在派生类中是不可访问的。保护继承是公有继承和私有继承之间的一种折中方式,它允许派生类访问基类的公有和保护成员,但不允许派生类的对象这样做。

继承方式比较

继承方式基类公有成员基类保护成员基类私有成员
公有继承继承为公有成员继承为保护成员不继承
保护继承继承为保护成员继承为保护成员不继承
私有继承继承为私有成员继承为私有成员不继承

面试题35:继承的优缺点是什么

继承在面向对象编程中具有重要的地位,它提供了代码重用和扩展性的机制。然而,就像任何其他编程概念一样,继承也有其优点和缺点。
优点:
(1)代码重用:继承允许子类重用父类的代码,这避免了在多个类中重复编写相同的代码。
(2)扩展性:通过继承,子类可以添加或覆盖父类的行为,从而实现对父类功能的扩展或修改。
(3)多态性:继承是实现多态性的基础,允许在运行时根据对象的实际类型来调用相应的方法。
(4)层次结构:继承有助于创建类的层次结构,这有助于组织和管理代码,并使得程序结构更加清晰。
(5)简化设计:通过继承,可以将复杂系统分解为更小的、更易于管理的部分。
缺点:
(1)代码耦合:继承可能导致代码之间的耦合度过高。如果父类发生变化,可能需要修改所有子类以适应这些变化。
(2)破坏封装性:继承可能会破坏封装性,因为子类可以访问父类的私有和保护成员。这可能导致父类的内部实现细节暴露给子类,增加了代码的复杂性。
(3)单一继承的限制:在C++中,类只能继承自一个基类(单继承)。这限制了代码的组织方式和可能的扩展性。
(4)菱形继承问题:在多重继承中,可能会出现菱形继承问题,即一个类从两个或多个路径继承同一个基类。这可能导致名称冲突和歧义。
(5)不易维护:如果继承层次结构过于复杂,可能会增加代码的维护难度。子类可能会依赖于父类的特定实现细节,而这些细节可能会在未来的版本中发生变化。
综上所述,继承是一种强大的机制,但也需要在使用时谨慎考虑其优缺点。在设计中,应该根据具体情况来决定是否使用继承,并尽量避免其潜在的问题。

面试题36:多态在C++中是如何实现的

多态( Polymorphism )是面向对象编程的三大基本特性之一,它允许我们使用相同的接口来表示不同类型的对象。在 C++ 中,多态通常通过虚函数( virtual functions )和指针或引用来实现。
虚函数是 C++ 中实现多态的关键机制。通过在基类的成员函数前加上 virtual 关键字,可以将其声明为虚函数。当派生类重写( override )这个虚函数时,就可以通过基类指针或引用来调用派生类的实现,这就是所谓的动态绑定或运行时多态。与之相对应的是静态绑定,即在使用父类指针或引用调用子类对象的成员函数时,如果没有使用虚函数,则会进行静态绑定,从而只能调用父类的成员函数,无法调用子类特有的成员函数。
虚函数的原理主要涉及虚函数表和虚函数指针:
(1)虚函数表
当一个类含有至少一个虚函数时,编译器会为这个类创建一个虚函数表( vtable ),并在每个该类的对象中嵌入一个指向这个虚函数表的指针(通常被称为 vptr )。
虚函数表是一个函数指针数组,其中每个元素都是指向类中定义的虚函数的指针。每个类,包括它的所有派生类,都会有自己的虚函数表。当派生类重写基类的虚函数时,派生类的虚函数表会包含指向这些重写函数的指针。
(2)虚函数指针
虚函数指针( virtual function pointer ,简称为 vptr )是一个隐藏的成员变量,它存在于包含至少一个虚函数的类的对象中。 vptr 指向一个虚函数表( vtable ),该表包含了类中所有虚函数的地址。虚函数表是一个函数指针数组,每个元素都指向一个虚函数的实现。
vptr 的存在是实现多态性的关键,它允许程序在运行时动态地确定应该调用哪个类的虚函数实现。当通过基类指针或引用调用一个虚函数时,实际调用的是 vptr 所指向的虚函数表中的函数。
虚函数的具体工作原理如下:
(1)定义虚函数:在基类中声明一个或多个虚函数。
(2)创建虚函数表:编译器为包含虚函数的类创建一个虚函数表。这个表是一个函数指针数组,每个元素指向类中的一个虚函数。
(3)初始化虚函数指针:当创建类的对象时,编译器会为该对象的虚函数指针成员变量分配内存,并初始化虚函数指针以指向该类的虚函数表。
(4)动态绑定:当通过基类指针或引用调用虚函数时,程序会查找虚函数指针所指向的虚函数表,并调用表中对应的函数。这个查找过程是在运行时进行的,因此被称为动态绑定。
C++中的多态主要有两种类型:静态多态和动态多态。
静态多态
也称为编译时多态或早绑定( Early Binding )。这主要通过函数重载( Function Overloading )和模板( Templates )来实现。在编译时,编译器就能确定应该调用哪个函数。
动态多态
也称为运行时多态或晚绑定( Late Binding )。这是通过虚函数( Virtual Functions )和继承来实现的。动态多态也被称为函数重写( Function Overriding )。在运行时,程序根据对象的实际类型来确定应该调用哪个函数。
如下为样例代码:

#include <iostream>  class BaseClass
{
public:void overloadFunc(){printf("BaseClass overloadFunc()\n");}virtual void overrideFunc(){printf("BaseClass overrideFunc()\n");}
};class DerivedClass : public BaseClass
{
public:void overloadFunc()			// 函数重载{printf("DerivedClass overloadFunc()\n");}void overrideFunc()			// 函数重写{printf("DerivedClass overrideFunc()\n");}
};int main() 
{	BaseClass* obj = new DerivedClass;obj->overloadFunc();					// 调用基类的函数((DerivedClass*)obj)->overloadFunc();	// 强制类型转换后调用继承类的重载函数obj->overrideFunc();					// 调用继承类的重写函数delete obj;obj = nullptr;return 0;
}

上面代码的输出为:

BaseClass overloadFunc()
DerivedClass overloadFunc()
DerivedClass overrideFunc()

在上面代码中, BaseClass 是一个基类,它有一个成员函数 overloadFunc() 以及一个虚函数 overrideFunc()。 DerivedClass 是 BaseClass 的派生类,它重载了 overloadFunc() 函数,并且重写了 overrideFunc() 函数。在 main 函数中,使用 BaseClass 基类的指针变量创建了一个 DerivedClass 派生类的对象,并调用其 overloadFunc() 以及 overrideFunc(),注意使用强制类型转换后才能真正调用到继承类的重载函数,而重写函数则可以直接调用。

面试题37:静态多态和动态多态有什么区别

静态多态和动态多态是面向对象编程中两种重要的多态形式,它们在实现机制、运行时期和适用场景等方面存在显著的差异。
实现机制:
静态多态:也称为编译时多态,主要通过函数重载和运算符重载实现。在编译时期,根据函数参数的类型和数量或运算符的类型,编译器可以确定应该调用哪个函数或运算符。
动态多态:也称为运行时多态,主要通过虚函数和继承实现。在运行时,根据对象的实际类型(即动态类型),动态地确定调用哪个虚函数。
运行时期:
静态多态:在编译时期就可以确定函数的调用地址,并生成代码。因此,地址是早绑定的。
动态多态:函数调用的地址不能在编译期间确定,需要在运行时才能确定,属于晚绑定。
适用场景:
静态多态:适用于编译时期就能确定函数调用的场景,如函数重载和运算符重载。它主要用于增强代码的灵活性和可读性。
动态多态:适用于需要在运行时才能确定函数调用的场景,如通过基类指针或引用调用派生类的虚函数。它主要用于实现继承和多态性,以实现代码的扩展性和灵活性。
总之,静态多态和动态多态在面向对象编程中都扮演着重要的角色。静态多态主要关注编译时期的函数选择,而动态多态则关注运行时期的动态行为。理解它们的区别和适用场景有助于更好地设计和实现面向对象程序。

面试题38:为什么要将析构函数声明为虚函数

将析构函数定义为虚函数是一个重要的代码构建技术点,特别是在设计基类时。这是因为在多态性的场景中,当使用基类指针或引用指向派生类对象时,如果没有虚析构函数,可能会导致派生类对象的析构过程不完整,从而引发资源泄漏和其他问题。
当基类的析构函数不是虚函数时,如果通过基类指针删除派生类对象,只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类对象中的资源(如动态分配的内存)没有被正确释放,从而产生资源泄漏。
通过将基类的析构函数声明为虚函数,可以确保当使用基类指针或引用删除派生类对象时,派生类的析构函数也会被调用。这是多态性的一个关键方面,它允许在运行时确定应该调用哪个类的析构函数。
如下为样例代码:

#include <iostream>  class BaseClass
{
public:// 将析构函数声明为虚函数  virtual ~BaseClass(){printf("virtual ~BaseClass() \n");}
};class DerivedClass : public BaseClass
{
public:~DerivedClass(){printf("virtual ~DerivedClass() \n");}
};int main() 
{	BaseClass* obj = new DerivedClass;delete obj;obj = nullptr;// 如果 BaseClass 的析构函数不是虚函数,这里只会调用 BaseClass 的析构函数,  // 而不会调用 DerivedClass 的析构函数,导致资源泄漏。  // 如果 BaseClass 的析构函数是虚函数,则会先调用 DerivedClass 的析构函数,  // 然后调用 BaseClass 的析构函数,确保资源被正确释放。 return 0;
}

上面代码的输出为:

virtual ~DerivedClass()
virtual ~BaseClass()

在上面代码中,BaseClass 类的析构函数被声明为虚函数。当通过基类指针 obj 删除派生类 DerivedClass 的对象时,由于析构函数是虚函数,所以会先调用 DerivedClass 的析构函数,然后再调用 BaseClass 的析构函数。这样,派生类中的资源能够被正确释放,避免了资源泄漏。
因此,通常建议在设计基类时将析构函数定义为虚函数,以确保在删除派生类对象时能够正确地调用析构函数链。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/679330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【个人笔记】计算机网络五层结构理解

#纯属个人笔记 作为学习记录用途 #较多个人比较好理解的说法 可能不太准确 若发现错误 欢迎评论区指正 不希望误导小白 详细的概念请以书本的定义为准 目录 正片 传统的五层结构 如下&#xff1a; 物理层&#xff08;Physical Layer&#xff09;&#xff1a; 负责传输比特流…

C#阿里云消息列队推送消息

推送消息到队列 IMNS nativeclient new Aliyun.MNS.MNSClient(accessKeyId, accessKeySecret, endpoint, _stsToken);var nativeSend nativeclient.GetNativeTopic("SMQ");nativeSend.PublishMessage("推送消息内容"); 需要引用Aliyun.MNS.dll 下载地址…

【Linux】信号保存与信号捕捉处理

信号保存与信号捕捉 一、信号保存1. 信号的发送2. 理解信号保存&#xff08;1&#xff09;信号保存原因&#xff08;2&#xff09;信号保存概念 3. 信号保存系统接口&#xff08;1&#xff09;sigset_t&#xff08;2&#xff09;sigprocmask()&#xff08;3&#xff09;sigpend…

论文阅读-GROUP:一种聚焦于工作负载组行为的端到端多步预测方法

摘要 准确地预测工作负载可以使网络服务提供商实现应用程序的主动运行管理&#xff0c;确保服务质量和成本效益。对于云原生应用程序来说&#xff0c;多个容器协同处理用户请求&#xff0c;导致每个容器的工作负载变化受到工作负载组行为的影响。然而&#xff0c;现有方法主要…

代码随想录算法训练营第三十四天丨16.1 活动选择、877. 石子游戏

16.1 活动选择 最优子结构 活动选择问题的最优子结构意味着问题的最优解包含了其子问题的最优解。具体来说&#xff0c;如果我们有一个按结束时间排序的活动集合 S{a1​,a2​,...,an​}&#xff0c;并且 S’ 是 S 的最大兼容活动子集&#xff0c;那么对于 S′ 中的任何活动aj…

Hyper-V 调整 设置 Ubuntu 虚拟机的分辨率

使用win10 的hyper-v安装 ubuntu20&#xff0c; 在ubuntu内无法调整display settings。可以使用以下方法 解决&#xff1a; 1.修改ubuntu系统中的grub文件&#xff1b; 使用命令行&#xff1a; sudo vi /etc/default/grub 找到GRUB_CMDLINE_LINUX_DEFAULT这一行&#xff0c;…

JAVA基本内容(数据类型、标识符、数组、注释、关键字)

目录 基本数据类型 取值范围大小 情景一&#xff1a; 情景二&#xff1a; 情景三&#xff1a; 情景四&#xff1a; 标识符 情景一&#xff1a; 情景二&#xff1a; 情景三&#xff1a; 数组 一维数组 二维数组 多维数组 注释 单行注释 多行注释 文档注释 关键字 基本数据类…

【Android】使用Termux终端搭建本地web服务器

在Android手机上有一个Termux APP&#xff0c;可运行类似 Linux 终端的模拟器&#xff0c;可以运行Nodejs&#xff0c;正好用它运行本地站点&#xff0c;用不着去租服务器&#xff0c;相比运行在电脑上&#xff0c;节省了电费&#xff0c;想要学来用的话不妨看看这篇文章。 文章…

案例:CentOS8 在 MySQL8.0 实现半同步复制

异步复制 MySQL 默认的复制即是异步的&#xff0c;主库在执行完客户端提交的事务后会立即将结果返给给客户端&#xff0c;并不关心从库是否已经接收并处理&#xff0c;这样就会有一个问题&#xff0c;主节点如果 crash 掉了&#xff0c;此时主节点上已经提交的事务可能并没有传…

Python:解析获取连续的重叠对pairwise

简介&#xff1a;pairwise函数&#xff0c;返回从输入迭代器获取的重叠对的迭代器&#xff0c;是Python 3.10 新特性&#xff0c;表示一个迭代器从对象中获取连续的重叠对&#xff0c;在某些场景中可以优化代码运行效率。pairwise 函数是一种用于处理列表中元素之间配对操作的通…

四元数如何进行标准化?

假设有一个四元数的张量 r&#xff0c;它包含了两个四元数&#xff1a; r [ 1 2 3 4 4 3 2 1 ] r \begin{bmatrix} 1 & 2 & 3 & 4 \\ 4 & 3 & 2 & 1 \\ \end{bmatrix} r[14​23​32​41​] 这里&#xff0c;第一个四元数是 q 1 ( 1 , 2 , 3 , 4 ) …

【Langchain Agent研究】SalesGPT项目介绍(二)

【Langchain Agent研究】SalesGPT项目介绍&#xff08;一&#xff09;-CSDN博客 上节课&#xff0c;我们介绍了SalesGPT他的业务流程和技术架构&#xff0c;这节课&#xff0c;我们来关注一下他的项目整体结构、poetry工具和一些工程项目相关的设计。 项目整体结构介绍 我们把…

互联网医院架构系统设计与实现

随着互联网技术的快速发展&#xff0c;互联网医院作为一种新兴的医疗服务模式&#xff0c;正逐渐受到人们的关注和使用。本文将介绍互联网医院架构系统的设计原则和关键组件&#xff0c;以及如何实现一个安全、高效和可扩展的互联网医疗服务平台。 内容&#xff1a; 1. 引言 …

LeetCode Python - 9.回文数

文章目录 题目答案运行结果 题目 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向左&#xff09;读都是一样的整数。 例如&am…

【开源】基于JAVA+Vue+SpringBoot的实验室耗材管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 耗材档案模块2.2 耗材入库模块2.3 耗材出库模块2.4 耗材申请模块2.5 耗材审核模块 三、系统展示四、核心代码4.1 查询耗材品类4.2 查询资产出库清单4.3 资产出库4.4 查询入库单4.5 资产入库 五、免责说明 一、摘要 1.1…

【JavaScript】文档对象模型(DOM)的基本概念

文章目录 1. 什么是DOM2. DOM的层次结构3. 获取DOM元素通过标签名获取元素通过ID获取元素通过类名获取元素 4. 操作DOM元素修改元素内容修改元素样式添加新元素 5. 事件处理6. 总结 文档对象模型&#xff08;DOM&#xff09;是 JavaScript 中一个重要的概念&#xff0c; 它允许…

【ECMAScript modules规范示例详解——介绍】

ECMAScript modules规范示例详解——介绍 1. 介绍2. 创建一个模块&#xff08;module.js&#xff09;3. 导入模块&#xff08;main.js&#xff09;4. 说明 1. 介绍 ECMAScript 模块 (ESM) 是 JavaScript 的官方标准&#xff0c;用于在 JavaScript 应用程序中导入和导出模块&am…

ChatGPT 4:新特性与优势

ChatGPT 4&#xff1a;新特性与优势 一、引言 ChatGPT 4是一款备受瞩目的人工智能模型&#xff0c;它以其强大的语言生成能力和智能回答能力&#xff0c;为用户提供了更高效、更便捷的对话体验。为了能够充分享受ChatGPT 4的各项功能&#xff0c;本文将向您详细介绍其新特性&…

Ps:信息面板

Ps菜单&#xff1a;窗口/信息 Window/Info 快捷键&#xff1a;F8 信息 Info面板提供了关于工作文档和当前操作的实时信息&#xff0c;包括鼠标指针的位置、取样点的颜色值&#xff08;包括调整前后的对比值&#xff09;&#xff0c;以及当前所用工具的提示信息等等&#xff0c;…

PyTorch深度学习实战(26)——多对象实例分割

PyTorch深度学习实战&#xff08;26&#xff09;——多对象实例分割 0. 前言1. 获取并准备数据2. 使用 Detectron2 训练实例分割模型3. 对新图像进行推断小结系列链接 0. 前言 我们已经学习了多种图像分割算法&#xff0c;在本节中&#xff0c;我们将学习如何使用 Detectron2 …