C++八股集锦

1、引用与指针的区别:

初始化:引用在定义时必须初始化,指针则没有要求(尽量初始化,防止野指针)
引用在初始化引用一个实体后,就不能再引用其它实体,而指针可以在任意时候指向一个同类型实体
没有NULL引用,但是有nullptr指针
在sizeof中含义不同: 引用结果为引用类型的大小,但指针始终是地址空间,所占字节个数(32位平台占4个字节)
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
有多级指针,但没有多级引用
访问实体的方式不同,指针需要显式解引用,引用编译器自己处理
引用比指针使用起来相对安全

引用和指针的相同点
1、指针和引用都可以优化传参效率
2、都是地址的概念;指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。
3、指针和引用都占内存空间

2、面向对象的三大特征:继承、封装、多态

封装:对外提供接口,屏蔽数据,对内开放数据。C++的封装的本质,在于将数据和行为绑定在一起,再通过对象来完成操作。

继承(inheritance)机制是面向对象程序设计中使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生的新类,称派生类(或子类),被继承的类称基类(或父类)。

多态就是函数调用的多种形态,使用多态能够使得不同的对象去完成同一件事时,产生不同的动作和结果。

多态又分为静态多态和动态多态:
(1)静态多态,也称为静态绑定或前期绑定(早绑定):函数重载和函数模板实例化出多个函数(本质也是函数重载)。静态多态也称为编译期间的多态,编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。

(2)动态多态,也称为动态绑定或后期绑定(晚绑定):在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,即运行时的多态。在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。

父类指针或引用指向父类,调用的就是父类的虚函数
父类指针或引用指向子类,调用的就是子类的虚函数

 多态的构成条件

多态是指不同继承关系的类对象,去调用同一函数,产生了不同的行为。在继承中要想构成多态需要满足两个条件:

  1. 必须通过基类的指针或者引用调用虚函数。
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

3、类与对象

类是具有相同属性和服务的一组对象的集合

对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。

类与对象的区别:

1,类是一个抽象的概念,它不存在于现实中的时间/空间里,类只是为所有的对象定义了抽象的属性与行为。

2,对象是类的一个具体。它是一个实实在在存在的东西。

3,类是一个静态的概念,类本身不携带任何数据。当没有为类创建任何对象时,类本身不存在于内存空间中。

4,对象是一个动态的概念。每一个对象都存在着有别于其它对象的属于自己的独特的属性和行为。对象的属性可以随着它自己的行为而发生改变。

4、智能指针:

因为C++使用内存的时候很容易出现野指针、悬空指针、内存泄露的问题。所以C++11引入了智能指针来管理内存。有四种:

  • auto_ptr:已经不用了
  • unique_ptr:独占式指针,同一时刻只能有一个指针指向同一个对象
  • shared_ptr:共享式指针,同一时刻可以有多个指针指向同一个对象
  • weak_ptr:用来解决shared_ptr相互引用导致的死锁问题
  • 死锁:线程/进程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待。

5、 strcpy 、memcpy 和 memset 区别

memset用于将一块内存设置为特定的值,

memcpy用于将一块内存从一个位置复制到另一个位置,

strcpy用于将一个字符串从一个位置复制到另一个位置。

memset函数常用于POD类型对象的初始化,一般第二个参数都是0,第三个参数是这段内存的长度。

memcoy函数需要注意的点是,函数传入的源位置和目标位置不能有重叠,否则这种操作引发的结果无法预知。如果你不能够确定传入的源位置和目标位置是否存在重叠,那么可以使用memmove函数代替memcoy。

strcpy存在另一个问题,那就是源字符串的长度可能大于目标区域的长度,导致目标区域内存被超写,造成不可预知的错误。当然也可以通过使用strncpy函数传入目标区域的大小

6、线程安全从哪些方面去判断?

竞态条件:竞态条件发生在多个线程同时访问共享资源,且至少有一个线程对资源进行写操作。线程安全的代码应该避免竞态条件,通常通过使用同步机制(例如互斥锁、信号量等)来确保只有一个线程可以访问关键区域。

原子操作:原子操作是不可中断的操作,要么全部执行,要么不执行,不会在执行过程中被其他线程干扰。

可见性:可见性指的是一个线程对共享数据的修改应该对其他线程可见。

死锁:死锁是多个线程互相等待彼此持有的资源,导致程序无法继续执行的情况。

内存管理:线程安全的代码应该注意内存管理,避免内存泄漏和访问已释放内存的情况。

数据共享:多线程环境下的数据共享需要谨慎管理,确保多个线程对共享数据的访问是安全的。

7、C++内存分配方式

C/C++内存分配有三种方式:
[1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
[2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。

[3]从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。

8、C++中的虚函数表和虚函数在内存中的位置

 虚函数表位于只读数据段(.rodata),即:C++内存模型中的常量区;
虚函数代码则位于代码段(.text),也就是C++内存模型中的代码区;

9、如何避免野指针或者悬空指针?

初始化指针: 在声明指针变量后,确保立即将其初始化为 nullptrNULL

使用智能指针: C++中提供了智能指针(如std::shared_ptrstd::unique_ptr),它们可以自动管理内存的生命周期,当对象不再需要时,会自动释放内存,从而避免野指针问题。

避免返回局部变量的指针: 不要返回指向函数内部局部变量的指针,因为这些变量在函数退出后将被销毁。

及时置空指针: 当你不再需要一个指针时,将它置为空指针,以避免在后续的代码中不小心访问已释放的内存。

使用引用: 在某些情况下,可以使用引用而不是指针来避免野指针问题。

10、编译一个代码的流程

编写完成一个C/C++程序后,想要运行起来,必须要经过四个步骤:预处理、编译、汇编和链接。每个步骤都会生成对应的文件,如下图所示(注意后缀名):

 预编译把一些#define的宏定义完成文本替换,然后将#include的文件里的内容复制到.cpp文件里,如果.h文件里还有.h文件,就递归展开生成.i文件

编译只是把我们写的代码转为汇编代码它的工作是检查词法和语法规则,所以,如果程序没有词法或者语法错误,那么不管逻辑是怎样错误的,都不会报错生成.s文件

汇编过程将上一步的汇编代码(main.s)转换成机器码(machine code),这一步产生的文件叫做目标文件(main.o),是二进制格式生成.o文件

经过汇编之后生成的目标文件(*.o)并不是最终的可执行二进制文件,而仍是一种中间文件(或称临时文件),目标文件仍然需要经过链接(Link)才能变成可执行文件链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件(.o)和系统组件组合成一个可执行文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值