c++ 面试题

1、c++程序编译过程

编译过程分为四个过程:编译(编译预处理、编译、优化),汇编,链接

  • 编译预处理: 处理以#开头的指令。
  • 编译、优化: 将源代码.cpp文件翻译成.s汇编代码。
  • 汇编: 将汇编代码.s翻译成机器指令.o文件。
  • 链接: 汇编程序生成的目标文件,即.o文件。并不会立即执行,因为可能会出现:.cpp文件中的函数引用了另一个.cpp文件中定义的符号或者调用了某个库文件中的函数。那链接的目的就是将这些文件对应的目标文件连接成一个整体,从而生成可执行程序.exe文件。

链接分为两种:

  • 静态链接: 代码从其所在的静态链接库中拷贝到最终的可执行程序中,在该程序被执行时,这些代码会被装入到该进程的虚拟地址空间中。
  • 动态链接: 代码被放到动态链接库或共享对象的某个目标文件中,链接程序只是在最终的可执行程序中记录了共享对象的名字等一些信息。在程序执行时,动态链接库的全部内容会被映射到运行时相应进行的虚拟地址的空间。

二者的优缺点:

  • 静态链接: 浪费空间,每个可执行程序都会有目标文件的一个副本,这样如果目标文件进行了更新操作,就需要重新进行编译链接生成可执行程序(更新困难);优点就是执行的时候运行速度快,因为可执行程序具备了程序运行的所有内容。
  • 动态链接: 节省内存、更新方便,但是动态链接是在程序运行时,每次执行都需要链接,相比静态链接会有一定的性能损失。

2、c++内存管理

c++内存分区:栈、堆、全局/静态存储区、常量存储区、代码区

  • 栈: 存放函数的局部变量、函数参数、返回地址等,有编译器自动分配和释放。
  • 堆: 动态申请的内存空间,就是由malloc分配的内存块,由程序员控制它的分配和释放,如果程序员执行结束还没有释放,操作系统会自动回收。
  • 全局区/静态存储区(.bss段和.data段): 存放全局变量和静态变量,程序运行结束操作系统自动释放,在c语言中,未初始化的放在.bss段中,初始化的放在.data段中,c++中不再区分了。
  • 常量存储区(.data段): 存放的是常量,不允许修改,程序运行结束自动释放。
  • 代码区(.text段): 存放代码,不允许修改,但可以执行。编译后的二进制文件存放在这里。

在这里插入图片描述

3、栈和堆的区别

  • 申请方式:栈是系统自动分配,堆是程序员主动申请。
  • 申请后系统响应:分配栈空间,如果剩余空间大于申请空间则分配成功,否则分配失败栈溢出;申请堆空间,堆在内存中呈现的方式类似链表(记录空闲地址空间的链表),在链表上寻找第一个大于申请空间的节点分配给程序,将该节点从链表中删除,大多数系统中该块空间的首地址存放的是本次分配空间的大小,便于释放,将该块空间上的剩余空间再次连接在空闲链表上。
  • 栈在内存中是连续的一块空间(向低地址扩展)最大容量是系统预定好的(2M),堆在内存中的空间(向高地址扩展)是不连续的。
  • 申请效率:栈是由系统自动分配,申请效率高,但程序员无法控制;堆是由程序员主动申请,效率低,使用起来方便但是容易产生碎片。
  • 存放的内容:栈中存放的是局部变量,函数的参数;堆中存放的内容有程序员控制。

4、变量的区别(全局变量、局部变量、静态全局变量、静态局部变量的区别)

c++变量根据定义的位置的不同具有不同的作用域,作用域可分为6种:全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域。

从作用域看:

  • 全局变量:具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern关键字再次声明这个全局变量。
  • 静态全局变量:具有文件作用域。它与全局变量的区别在于如果程序包含很多个文件的话,它作用于定义它的文件里,不能作用到其它文件里(即被static关键字修饰过的变量具有文件作用域)。即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
  • 局部变量:具有局部作用域。出了其作用域就被销毁。
  • 静态局部变量:具有局部作用域。它只被初始化一次,自动第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数可见。

从内存空间看:

  • 静态存储区:全局变量,静态局部变量,静态全局变量。
  • 栈:局部变量。

5、全局变量定义在文件中有什么问题

如果在头文件中定义全局变量,当该头文件被多个文件include时,该头文件中的全局变量就会被定义多次,导致重复定义,因此不能在头文件中定义全局变量。

6、内存对齐

什么是内存对齐

  • 结构体每个成员相对于结构体首地址的偏移量都是该成员大小中较小者的整数倍。

为什么要进行内存对齐

  • 不是所有的硬件平台都能访问任意地址上的任意数据的。

内存对齐的优点

  • 便于在不同的平台之间进行移植
  • 提高内存的访问效率

7、类大小的计算

  • 遵循结构体的字节对齐原则
  • 与普通成员变量有关,与成员函数和静态成员无关。
  • 虚函数对类的大小有影响,虚表指针
  • 虚继承对类的大小有影响,虚基类指针
  • 空类的大小是1字节

8、什么是内存泄漏

由于疏忽或错误导致的程序未能释放已经不再使用的内存。

9、智能指针有哪几种

  • 共享指针(shared_ptr):资源可以被多个智能指针共享,使用引用计数的方式。当共享指针声明周期结束时,引用计数-1,当引用计数为0时,释放内存空间。
  • 独占指针(unique_ptr):独享所有权的智能指针,资源智能被一个智能指针占有,该智能指针不能拷贝构造和赋值,但可以进行移动构造和移动赋值构造。
  • 弱指针(weak_ptr):指向shared_ptr指向的对象,能够解决有shared_ptr引起的循环引用问题。

10、c++11新特性

  • auto类型推导
  • lambda表达式
  • 范围for语句
  • 右值引用
  • 标准库move()函数:通过该函数可以获得绑定到左值上的右值引用。
  • 智能指针
  • delete 函数 和 default 函数(禁用该函数/生成默认的函数)

11、c和c++的区别

  • c语言是面向过程的编程;c++是面向对象的编程。
  • c语言主要用于嵌入式领域、驱动开发等与硬件直接打交道的领域;c++可以用于应用层开发,用户界面开发等,与操作系统打交道的领域。
  • c++在c的基础上增加了很多特性和关键字,使其功能更加强大。

12、什么是面向对象?面向对象的三大特性

       对象是指具体的某一事物,这些事物的抽象就是类,类中包含成员变量和成员函数。

  • 封装:将具体的实现过程和数据封装成一个类,只能通过接口进行访问;降低耦合性。
  • 继承:是面向对象程序设计中是代码可以复用的重要手段,它允许程序员在原有类特性的基础上进行扩展功能,并且可以重写基类的的方法。继承是多态的前提;增加了类的耦合性。
  • 多态:不同继承对象对同一消息做出的不同响应,基类指针指向派生类对象,呈现不同的表现形式;实现了接口的重用。

13、重载、重写、隐藏的区别

  • 重载:相同的函数名,不同的参数类型和数量。
  • 隐藏:派生类的函数屏蔽了与其同名的基类函数,只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。
  • 重写(覆盖):是指派生类中存在重新定义的的函数。函数名、参数列表、返回值类型都与基类中被重写的函数一致,只有函数体不同。重写的基类中被重写的函数必须有virtual修饰。

区别:

  • 参数列表不同、范围不同(类内和类之间)。

14、什么是多态?多态如何实现

  • 多态就是不同继承类的对象,对同一消息做出不同的响应,基类的指针指向派生类对象,使得基类指针呈现不同的表现方式。
  • 多态是通过虚函数实现的,通过添加virtual关键字使函数称为虚函数,在派生类中重写该虚函数的实现;虚函数的地址保存在虚函数表中。

15、sizeof和strlen的区别

  • sizeof是运算符;strlen是库函数。
  • sizeof测量的是字符数组的分配空间大小;strlen测量的是字符串的实际长度。

16、explicit的作用

       用来声明类构造函数时显示调用的,而非隐式调用;可以阻止调用构造函数时进行隐式转换。只可用于修饰单参构造函数。

17、static的作用

  • 定义静态变量,静态函数。
  • static作用于局部变量,改变了局部变量的生命周期,使该变量在程序结束后才销毁。
  • static作用于全局变量和函数,改变了全局变量和函数的作用域,使全局变量和函数只能在定义它的文件中使用,在源文件中不具有全局可见性。
  • static作用于类的成员变量和成员函数,使类变量和类成员函数和类有关,可以不定义类的对象就可以访问这些静态成员。

18、static在类中使用的注意事项

  • 静态成员变量在类内进行声明,在类外进行定义和初始化。
  • 静态成员变量相当于类域中的全局变量,被类的所有对象所共享,包括派生类的对象。

19、const作用及用法

  • const修饰成员变量,定义成const常量,相当于宏常量,可进行类型检查,节省内存空间,提高了效率。
  • const修饰函数参数,使传递过来的函数参数的值不能改变。
  • const修饰成员函数,使成员函数不能修改任何类型的成员变量(mutable修饰的变量除外),也不能调用非const成员函数,因为非const成员函数可能会修改成员变量。

20、define和const的区别

  • define是在编译预处理阶段进行替换;const是在编译阶段确定其值。
  • define定义的宏常量没有数据类型,只是进行简单的替换,不会进行类型安全检查;const定义的常量是有类型的,可以避免一些低级的错误。
  • define定义的宏常量,在程序中使用多次就会进行多少次替换,内存中有多个备份,占用代码段的空间;const定义的常量占用静态存储区的空间,程序运行过程中只有一份。
  • define定义的宏常量不能调试,因为在预处理阶段就已经进行替换了;const定义的常量可以进行调试。

21、define和typedef的区别

  • #define是预处理指令,在预处理阶段进行替换,不作正确性检查。
  • typedef是关键字,在编译时处理,有类型检查功能,用来给一个已经存在的类型起一个别名。

22、用宏实现比较大小,得到最大值最小值

  • #define MAX(X,Y) ((X)>(Y)?(X):(Y))
  • #define MIN(X,Y) ((X)<(Y)?(X):(Y))

23、inline作用及使用方法

作用:

  • inline是一个关键字,可以用于定义内联函数。内联函数像普通函数一样被调用,但是在调用时并不通过函数调用的机制而是直接在调用点处展开,这样可以大大减少由函数调用带来的开销,从而提高程序的运行效率。

使用方法:

  • 在类内定义成员函数,可以不用在函数头部加inline关键字,因为编译器会自动将类内定义的函数声明为内联函数。
  • 当在类内声明函数,在类外定义函数时,如果想将该函数定义为内联函数,则可以在类内声明时不加inline关键字,而在类外定义函数时加上inline关键字。另外,可以在声明函数和定义函数同时加上inline;也可以只在函数声明时加inline,而定义函数时不加inline。只要确保在调用该函数之前把inline的信息告知编译器即可。

24、inline函数工作原理

  • 内联函数不是在调用时发生控制转移关系,而是在编译阶段将函数体嵌入到每一个调用该函数的语句块中,编译器会将程序中出现内联函数的调用表达式用内联函数的函数体来替换。
  • 普通函数时将程序执行转移到被调用函数所存放的内存地址,当函数执行完后,返回到执行此函数前的地方。转移操作需要保护现场,被调函数执行完后,再恢复现场,该过程需要较大的资源开销。

25、new和malloc如果判断申请到内存

  • malloc成功返回内存的指针,否则返回NULL。
  • new成功返回该对象类型的指针,否则抛出异常。

26、delete实现原理?delete和delete[]的区别?

原理:

  • 首先执行该对象所属类的析构函数。
  • 进而通过调用operator delete中的标准库函数来释放所占的内存空间。

区别:

  • delete用来释放单个对象所占空间,只会调用一次析构函数。
  • delete[]用来释放数组空间,会对数组中的每个成员都调用一次析构函数。

27、new和malloc的区别,delete和free的区别

  • malloc和free是库函数,而new和delete是关键字。
  • new申请内存时,返回的类型是对象的指针类型,无需强制转换;而malloc申请空间返回的是void*类型,需要进行强制转换为对象的类型指针。
  • new分配失败时会抛出异常;而malloc分配失败返回NULL。
  • 对于自定义类型,new首先调用operator new()函数申请空间(底层通过malloc实现),然后调用构造函数进行初始化;delete首先调用operator delete()函数释放空间(底层通过free实现)。malloc和free只能开辟和释放空间,不能调用构造和析构函数。
  • new从自由存储区上分配内存;malloc从堆上分配内存。

28、c和c++中struct的区别

  • 在c语言中,struct是用户自定义数据类型;在c++中struct是抽象数据类型,支持成员函数的定义。
  • 在c语言中,struct没有访问权限的设置,是一些变量的集合体,不能定义成员函数;在c++中struct可以和类一样,有访问权限,并可以定义成员函数。
  • 在c语言中,struct定义的自定义数据类型,在定义该类型的变量时,需要加上struct关键字;在c++中不用加该关键字。

29、为什么有了class还保留struct?

  • c++是在c语言基础上发展起来的,为了与c语言兼容,c++保留了struct。

30、struct和union的区别

  • 联合体和结构体都是由若干个数据类型不同的数据成员组成。使用时,联合体只有一个有效的成员;而结构体所有的成员都有效。
  • 对联合体的不同成员赋值,将会覆盖其它成员的值,而对结构体的不同成员赋值时,相互不影响。
  • 联合体的大小是其内部所有变量的最大值,按照最大类型的倍数进行分配大小;结构体分配内存的大小遵循字节对齐原则。

31、class和struct的异同

  • struct和class都可以自定义数据类型,也支持继承操作。
  • struct默认的访问级别是public,默认的继承级别也是public;class默认的访问级别是private,默认的继承级别也是private。
  • 当class继承struct或者继承class时,默认的继承级别取决于派生类的默认继承级别。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值