C++高频面试知识总结 part2

C++高频面试

  • 1.sizeof是什么?sizeof一个class大小怎么确定?是在编译期还是在运行期确定?
  • 2.函数重载的机制,重载是在编译期还是在运行期确定,重载有额外开销吗
  • 3.函数重写在编译还是运行时确定?
  • 4.如何找到虚函数表,如何找到虚函数指针
  • 5.虚函数表存在什么内存区域上,如果一个子类没有重写虚函数,会和基类公用一个虚函数表吗?
  • 6.虚指针在哪初始化?
  • 7.vector扩容,插入n次远远大于当前长度,求扩容次数

1.sizeof是什么?sizeof一个class大小怎么确定?是在编译期还是在运行期确定?

1.1 sizeof是一个在C和C++等编程语言中使用的运算符,它用于获取存储在内存中某个类型或对象所占用的字节大小。当你使用sizeof运算符时,它会返回一个size_t类型的值,这个值表示的是字节数

1.2 对于一个类来说,其大小是在编译期间确定的,因为编译器需要知道每个类型的确切大小以便于分配内存空间

1.3 一个类的sizeof是由多个因素决定的,主要包括:

类中所有非静态成员变量的大小:每个成员变量都有自己的数据类型,sizeof运算符可以用来计算这些类型的字节大小,类的大小至少是这些成员变量大小的总和。

内存对齐:为了提高访问效率,许多系统会对数据进行对齐处理。例如,某些系统要求所有类型的数据按照其大小的倍数进行对齐。这意味着如果一个成员变量是一个char类型(通常是1字节),而下一个成员变量是一个double类型(通常是8字节),那么char变量后面可能会有额外的填充字节,以确保double变量按照其自身大小的倍数进行对齐

虚函数:如果类包含虚函数,编译器会在这个类的对象中插入一个虚函数表指针(vptr)。这个指针指向一个虚函数表,该表包含了类中所有虚函数的地址。这个额外的指针会增加类的大小

继承:如果一个类是从其他类派生出来的,派生类的大小至少是其直接基类间接基类的大小加上派生类自己添加的成员变量大小。需要注意的是,虽然派生类包含了基类的所有成员,但基类的成员不会重复计算,也就是说,派生类的大小不会是所有基类大小的简单累加。

C++类的大小——sizeof(class)

在编译期间,编译器会根据上述因素计算出一个类的总大小。这个大小是固定的,与运行时无关。因此,当你在程序中创建类的实例或将类的指针或引用传递给函数时,sizeof运算符会返回这个在编译时确定的大小

需要注意的是,sizeof并不考虑运行时可能发生的变化,例如动态分配的内存或者对象的动态大小。它只是简单地返回在编译时确定的、存储该类型或对象所需的字节大小。

举个例子,如果你有一个简单的类定义如下:

class MyClass {
public:int a;double b;char c;
};

2.函数重载的机制,重载是在编译期还是在运行期确定,重载有额外开销吗

函数重载是C++等编程语言中的一种特性,它允许在同一个作用域中定义多个具有相同名称参数列表不同的函数。

函数重载的确定是在编译期间进行的。编译器在处理函数调用时,会根据函数名和提供的参数类型数量来匹配对应的函数声明或定义。也就是C++编译时多态的体现。

在函数重载的过程中,可能会涉及到以下几个方面:
参数类型匹配:编译器会检查提供的参数类型是否与函数声明中的参数类型兼容。这可能涉及到类型转换,如隐式类型转换或显式类型转换(使用static_cast等)。

参数数量匹配:函数调用必须提供正确数量的参数,与函数声明或定义中列出的参数数量相匹配。

函数参数顺序:虽然参数顺序对于函数重载的解析不是决定性因素,但在某些情况下,参数顺序可能会影响最佳匹配的判断,尤其是在存在可变形参数(如C++中的std::initializer_list)的情况下。

至于开销,函数重载本身在运行时并没有额外的执行开销。一旦编译器在编译期间确定了调用哪个重载版本,运行时的函数调用就像调用任何其他函数一样。然而,函数重载可能会增加编译器的工作负担,因为它需要在多个候选函数中进行匹配,这可能会使得编译时间略有增加。
此外,如果重载的函数数量过多,可能会导致编译器报错,如模板实例化过多导致编译错误,或者在某些情况下,重载函数的选择可能会变得复杂,增加程序员的理解和调试难度。

3.函数重写在编译还是运行时确定?

重写(OverWrite):重写是子类对父类允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

方法的重写发生在运行时。因为在编译时,编译器是无法知道我们到底是调用父类的方法还是子类的方法,相反的,只有在实际运行的时候,我们才知道应该调用哪个方法。这个也是C++运行时多态的体现。

4.如何找到虚函数表,如何找到虚函数指针

虚函数表(vtable)是C++中实现动态多态性的关键组件之一。当你在类中声明至少一个虚函数时编译器为该类创建一个虚函数表,这个表是一个存储了指向类中所有虚函数的指针的数组。每个包含虚函数的类实例都包含一个指向其虚函数表的指针,称为虚函数指针(vptr)。虚函数表在编译的时候产生 ,否则这个类的结构信息中也不会插入虚指针的地址信息。

  • 虚函数表指针是虚函数表所在位置的地址。虚函数表指针属于对象实例。因而通过new出来的对象的虚函数表指针位于声明对象的虚函数表指针位于
  • 虚函数表位于只读数据段(.rodata),即:C++内存模型中的常量区
  • 虚函数代码则位于代码段(.text),也就是C++内存模型中的代码区
    在这里插入图片描述
    C++类的虚函数表和虚函数在内存中的位置

对于这种简单的类,其对象内存布局的最开始四个字节就是一个虚函数表指针(32位编译器),而这个指针变量的值自然就是虚函数表的地址了。

这里指针操作比较混乱,在此稍微解析下:
1. &father代表对象b的起始地址
2. (int *)&father 强转成int *类型,为了后面取b对象的前四个字节,前四个字节是虚表指针
3. *(int *)&father 取前四个字节,即vptr虚表地址

根据上面的解析我们知道*(int *)&father即虚表地址.并且虚表是存放虚函数指针的,所以虚表中每个元素(虚函数指针)在32位编译器下是4个字节,因此(int *)*(int *)&father,这样强转后为了后面的取四个字节.所以*(int *)*(int *)&father就是虚表的第一个元素,即FatherFun1()的地址,那么接下来的取第二个虚函数地址也就依次类推. 始终记着vptr指向的是一块内存, 这块内存存放着虚函数地址,这块内存就是我们所说的虚表.

*(int *)&father
#include <stdio.h>
#include <stdlib.h>
#include <iostream>using namespace std;class Father
{virtual void FatherFun1() {cout << "FatherFun1" <<endl;}virtual void FatherFun2() {cout << "FatherFun2" <<endl;}virtual void FatherFun3() {cout << "FatherFun3" <<endl;}
};typedef void (*Fun)(void);int main()
{Father father;cout << "类对象地址:" << &father << endl;cout << "虚函数表地址: " << (int*)*(int*)&father << endl;cout << "虚函数FatherFun1地址:" <<(int*)*(int*)&father << endl;cout << "虚函数FatherFun2地址:" <<(int*)*(int*)&father + 1 << endl;cout << "虚函数FatherFun3地址:" <<(int*)*(int*)&father + 2 << endl;cout << "测试地址是否正确" << endl;Fun fun = (Fun)*(int*)*(int*)&father;fun();fun = (Fun)*((int*)*(int*)(&father) + 1);fun();fun = (Fun)*((int*)*(int*)(&father) + 2);fun();system("pause");
}
类对象地址:003BF90C
虚函数表地址: 00867898
虚函数FatherFun1地址:00867898
虚函数FatherFun2地址:0086789C
虚函数FatherFun3地址:008678A0
测试地址是否正确
FatherFun1
FatherFun2
FatherFun3
请按任意键继续. . .

从这个输出我们可以看到,FatherFun1、FatherFun2、FatherFun3的地址只差了4个字节,且输出是正确的,那么第三点已确认!

5.虚函数表存在什么内存区域上,如果一个子类没有重写虚函数,会和基类公用一个虚函数表吗?

虚函数表(vtable)通常存储在程序的只读数据段(也称为代码段或文本段)中。这个区域是用于存储程序执行时不可变的数据,例如代码本身、字符串常量以及其他只读数据。虚函数表作为类的一部分,包含了指向类中所有虚函数的指针,因此需要被保护起来,以防止程序运行时被意外修改

那么在有继承情况下,只要基类有虚函数,子类不论实现或没实现,都有虚函数表。虚表里面存放的是父类的虚函数指针。
基类的虚函数表子类的虚函数表不是同一个表。

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
class CTest
{
public:CTest() {};~CTest() {};virtual void SFunc() {cout << "测试虚函数" << endl;};
private:int a;short b;
};class CTestC : public CTest
{
public:CTestC() { m_iValueC = 0; };~CTestC() {};private:int m_iValueC;
};typedef void(*Fun)(void);  //函数指针 
int main()
{CTest t;printf("CTest 虚表地址:%p\n", *(int *)&t);CTestC t0;printf("CTestC 虚表地址:%p\n", *(int *)&t0 );return 0;
}
[root@iZ2zeirmyddvdjh8g2zyxzZ virtual]# ./v
CTest 虚表地址:0x402088
CTestC 虚表地址:0x402070

虚函数表构成、地址详细说明

6.虚指针在哪初始化?

由于每个对象调用的虚函数都是通过虚表指针来索引的,也就决定了虚表指针的正确初始化是非常重要的。换句话说,在虚表指针没有正确初始化之前,我们不能够去调用虚函数。那么虚表指针在什么时候,或者说在什么地方初始化呢?

答案是在构造函数中进行虚表的创建和虚表指针的初始化

构造函数的调用顺序,在构造子类对象时,要先调用父类的构造函数,之后再完成子类的构造。在调用父类的构造函数时,编译器只“看到了”父类,并不知道后面是否后还有继承者,它初始化虚表指针,将该虚表指针指向父类的虚表
当执行子类的构造函数时,虚表指针被重新赋值指向自身的虚表

7.vector扩容,插入n次远远大于当前长度,求扩容次数

1.扩容机制回顾
当向vector中插入元素时,如果元素有效个数size空间容量capacity相等时,vector内部会触发扩容机制:

  • 开辟新空间
  • 拷贝元素
  • 释放旧空间。

注意:每次扩容新空间不能太大,也不能太小,太大容易造成空间浪费,太小则会导致频繁扩容而影响程序效率。

2.选择以倍数方式扩容
假设有n个元素需要像vector插入,倍增因子为m,则完成n个元素像vector的push_back操作需要扩容log以m为低n的次方。比如:以二倍方式扩容,当向vector插入1000个元素,需要扩容log以2为底1000次方,就是扩容10次,第i次增容会把m的i次方个元素搬移到新空间,n次push_back的总操作次数为:
在这里插入图片描述
使用2倍(k=2)扩容机制扩容时,每次扩容后的新内存大小必定大于前面的总和
而使用1.5倍(k=1.5)扩容时,在几次扩展以后,可以重用之前的内存空间了

因为STL标准并没有严格说明需要按何种方式进行扩容,因此不同的实现厂商都是按照自己的方式扩容的,即:linux下是按照2倍的方式扩容的,而vs下是按照1.5倍的方式扩容的。
在这里插入图片描述

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

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

相关文章

图解大型网站多级缓存的分层架构

前言 缓存技术存在于应用场景的方方面面。从浏览器请求&#xff0c;到反向代理服务器&#xff0c;从进程内缓存到分布式缓存&#xff0c;其中缓存策略算法也是层出不穷。 假设一个网站&#xff0c;需要提高性能&#xff0c;缓存可以放在浏览器&#xff0c;可以放在反向代理服…

Hive 之 UDF 运用(包会的)

文章目录 UDF 是什么&#xff1f;reflect静态方法调用实例方法调用 自定义 UDF&#xff08;GenericUDF&#xff09;1.创建项目2.创建类继承 UDF3.数据类型判断4.编写业务逻辑5.定义函数描述信息6.打包与上传7.注册 UDF 函数并测试返回复杂的数据类型 UDF 是什么&#xff1f; H…

记一次靶场渗透测试(1)

本环境为黑盒测试&#xff0c;在不提供虚拟机帐号密码的情况下进行黑盒测试拿到域控里面的flag。 环境搭建 内网网段&#xff1a;192.168.93.0/24 外网网段&#xff1a;192.168.1.0/24 攻击机&#xff1a; kali&#xff1a;192.168.1.10 靶场&#xff1a; CentOS(内)&am…

Linux笔记之制作基于ubuntu20.4的最小OpenGL C++开发docker镜像

Linux笔记之制作基于ubuntu20.4的最小OpenGL C开发docker镜像 —— 2024-04-03 夜 code review! 文章目录 Linux笔记之制作基于ubuntu20.4的最小OpenGL C开发docker镜像1.这里把这本书的例程代码放在了Dockerfile所在的文件夹内以使镜像预装例程代码2.创建Dockerfile3.构建Do…

PDF编辑和格式转换工具 Cisdem PDFMaster for Mac

Cisdem PDFMaster for Mac是一款功能强大的PDF编辑和格式转换工具。它为用户提供了直观且易于使用的界面&#xff0c;使常用功能触手可及&#xff0c;从而帮助用户轻松管理、编辑和转换PDF文件。 软件下载&#xff1a;Cisdem PDFMaster for Mac v6.0.0激活版下载 作为一款完整的…

15 个最佳 Word 文档恢复工具 [免费下​​载]

MS Word 文档恢复的重要性 对于严重依赖 Microsoft Word 创建和编辑文档的个人和企业来说&#xff0c;MS Word 文档恢复是一个至关重要的方面。 文件损坏、系统崩溃和其他意外事件可能会导致 Word 文档中存储的重要数据丢失。 及时恢复这些文档有助于节省时间、精力和资源。 本…

GD32F470_寻迹避障模块 TCRT5000红外反射传感器模块移植

2.6 红外循迹传感器 红外循迹传感器采用TCRT5000红外反射传感器&#xff0c;一种集发射与接收于一体的光电传感器&#xff0c;它由一个红外发光二极管和一个NPN红外光电三极管组成。检测反射距离1mm-25mm适用&#xff0c;传感器特设M3固定安装孔&#xff0c;调节方向与固定方便…

【绩效管理】帮助零售企业建立分层分类绩效考核体系项目纪实

购物中心张经理评价&#xff1a;“员工的绩效管理一直是困扰我公司的难题&#xff0c;我们只懂得怎么经营&#xff0c;至于怎么做人力资源管理&#xff0c;真是一点都不懂。这次华恒智信为我们提供的服务对我们的帮助很大。基于企业实际调研情况&#xff0c;华恒智信专家明确指…

前端学习之DOM编程-案例div移动

这个案例是当你的鼠标按压下去后&#xff0c;div跟着你的鼠标移动而移动&#xff0c;当你的鼠标抬起后&#xff0c;div不随着鼠标移动而移动。类似于电脑移动应用图标的感觉。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

day02-SpringCloud02(Nacos、Feign、Gateway)

1.Nacos 配置管理 Nacos 除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我们需要一种统一配置管理方案&#x…

比例多路阀控制器US-DAT2-A

液压比例阀放大器是一种用于精确控制液压系统的技术&#xff0c;它通过电信号实现对液压阀的连续量控制。接收来自控制器的低功率电信号&#xff0c;然后将其放大并转换为高功率信号&#xff0c;这个高功率信号足以驱动比例阀的开启和关闭。这种技术允许进行非常精细的调节&…

面经分享(Flask,轻量级Web框架)

1. Flask的核心特点 a. 轻量级&#xff1a;核心简洁&#xff0c;只提供了基本的功能&#xff0c;其他高级功能可以通过插件或扩展来添加。 b. 灵活性&#xff1a;允许开发者选择适合自己项目的组件和工具&#xff0c;没有强制的项目结构和设计模式。 c. 易于扩展&#xff1a;提…

深度剖析:网络安全中的红蓝对抗策略

红蓝对抗 红蓝对抗服务方案 在蓝队服务中&#xff0c;作为攻击方将开展对目标资产的模拟入侵&#xff0c;寻找攻击路径&#xff0c;发现安全漏洞和隐患。除获取目标系统的关键信息&#xff08;包括但不限于资产信息、重要业务数据、代码或管理员账号等&#xff09;外&#x…

ubuntu20.04.6将虚拟机用户目录映射为磁盘Z

文章目录 linux虚拟机设置为NAT模式安装sshd服务映射目录到windows磁盘安装samba套件修改配置文件smb.conf重启smbd并设置用户名和密码 windows映射遇到的问题1、设置好之后映射不成功2、smbd下载失败3、smbd密码配置问题4、当有改动时候&#xff0c;最好重启一下smbd服务 linu…

碧桂园服务净利降两成,关联交易收入仅占2.9%,发力增值服务充电桩日进超10万

自2018年分拆上市以来&#xff0c;碧桂园服务经历过非常高速的发展&#xff0c;曾是物管市场的“并购王”&#xff0c;但从2023年开始&#xff0c;希望从外延式的增长向内生式增长转型&#xff0c;将往期的经验与教训&#xff0c;通过投后管理沉淀下来&#xff0c;向高质量发展…

【Ambari】Ansible自动化部署大数据集群

目录 一&#xff0e;版本说明和介绍信息 1.1 大数据组件版本 1.2 Apache Components 1.3 Databases支持版本 二&#xff0e;安装包上传和说明 三&#xff0e;服务器基础环境配置 3.1global配置修改 3.2主机名映射配置 3.3免密用户名密码配置 3.4 ansible安装 四. 安…

2024年MathorCup妈妈杯数学建模思路C题思路解析+参考成品

1 赛题思路 (赛题出来以后第一时间在群内分享&#xff0c;点击下方群名片即可加群) 2 比赛日期和时间 报名截止时间&#xff1a;2024年4月11日&#xff08;周四&#xff09;12:00 比赛开始时间&#xff1a;2024年4月12日&#xff08;周五&#xff09;8:00 比赛结束时间&…

认知觉醒:开启自我改变的原动力-- 读书笔记

文章目录 关于本书以及作者内容第一章 大脑——一切问题的起源摘抄思考 第二章 生命留给我们的彩蛋摘抄思考 第三章 人类的终极能能力摘抄思考 第四章 专注力——情绪和智慧的交叉地带摘抄思考 第五章 学习力——学习不是一味地努力摘抄思考 第六章 行动力——没有行动世界只是…

DFS:floodfill算法解决矩阵联通块问题

floodfill&#xff0c;翻译为洪水灌溉&#xff0c;而floodfill算法本质上是为了解决在矩阵中性质相同的联通块问题。 一、图像渲染 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int dx[4]{0,0,1,-1};int dy[4]{1,-1,0,0};int prev;//记住初始值int m,…

《QT实用小工具·十六》IP地址输入框控件

1、概述 源码放在文章末尾 该项目为IP地址输入框控件&#xff0c;主要包含如下功能&#xff1a; 可设置IP地址&#xff0c;自动填入框。 可清空IP地址。 支持按下小圆点自动切换。 支持退格键自动切换。 支持IP地址过滤。 可设置背景色、边框颜色、边框圆角角度。 下面…