C++面试题
1.什么是虚函数?什么是纯虚函数
虚函数:在类的继承中,基类中的函数前加virtual声明的函数就是虚函数。
虚函数实现了运行的多态,同一函数调用在不同对象中表现出不同的行为
纯虚函数:在基类中声明但没有实现的虚函数。在声明时末尾加上 =0 表示纯虚函数
包含纯虚函数的类称为抽象类,不能实例化对象。派生类必须实例化抽象类中的所有虚函数才能实例化。
虚函数:可以有实现,允许在派生类中重写,支持运行时多态。
纯虚函数:没有实现,强制派生类必须实现,基类因此成为抽象类。
2. 基类为什么需要析构函数?
析构函数是用来释放资源空间的。如果没有析构函数可能会造成内存泄漏及其他资源管理问题。
在派生类中有重写虚函数,当用基类指针定义派生类对象后,释放基类指针的派生类对象时,如果基类中的析构函数不是虚函数,会导致派生类中基类的空间不能被释放,可能会导致内存泄漏。
3.如何初始化const和static数据成员
const修饰的数据初始化后不能被修改,必须在构造函数中使用初始化列表进行初始化
static 数据成员属于类,而不是类的对象。所有对象共享同一个 static 数据成员。在类内部声明,但 在类外进行定义和初始化。
4.指针和引用的区别
指针是一个变量,其值为另一个变量的地址。可以通过指针间接访问和操作该变量。
引用是某个变量的别名,它为已有变量提供了一个新的名字。引用一旦初始化后就不能再指向其他变量。
使用 *
来声明指针,并使用 &
获取变量的地址。
使用 &
来声明引用,初始化时必须绑定到一个已有的变量。
可以是 nullptr
,未初始化的指针是一个野指针。
不能为 nullptr
,引用在创建时必须初始化。
可以改变指针指向的地址,可以随时将指针指向不同的变量。
不能重新赋值。引用一旦绑定到一个变量,就不能再指向其他变量。
指针在内存中占用固定的大小。
5.new和malloc的区别
new
和 malloc
都用于动态内存分配,
**new**
是 C++ 的运算符,在使用时会调用构造函数。返回所分配内存的指针,并自动进行类型转换。分配内存后,可以初始化对象(调用构造函数)。使用 delete
运算符释放内存,同时调用析构函数。使用 new[]
来分配数组内存,并在释放时使用 delete[]
。
malloc
:是 C 语言中的标准库函数,属于 C 语言的一部分,用于动态内存分配。返回 void*
类型的指针,使用时需要手动转换为所需类型。不会初始化分配的内存,返回的内存内容是未定义的。使用 free
函数释放内存,不会调用析构函数。使用 malloc
也可以分配数组内存,释放时直接free。
6.内存泄漏怎么产生的?如何避免?
内存泄漏是在释放动态申请的内存时没有正确释放
在new或者malloc空间后没有调用相应的delete和free进行释放
同一指针用来申请空间前没有释放原有的空间
避免方法:通过使用智能指针、遵循 RAII 原则、利用 STL 容器、以及使用静态分析工具,可以有效地避免和检测内存泄漏问题。
7.C++的内存分区
通常分为:栈区,堆区,全局区,代码区,未初始化数据区
8.常用的数据结构有哪些?时间复杂度和空间复杂度如何使用
常用的数据结构包括数组、链表、栈、队列、哈希表、树和堆等
数组访问时间复杂度为 O(1),但插入和删除操作在开头或中间的时间复杂度为 O(n),适用于随机访问:需要快速访问元素(如查找、修改)时。
链表插入和删除操作更高效(O(1)),但访问时间复杂度为 O(n),适用于频繁的插入和删除:如实现队列、栈中。
栈和队列操作时间复杂度均为 O(1),栈适用于存储函数的调用状态(如递归)及返回地址,队列适用于如操作系统中的进程调度。
哈希表使用哈希函数实现快速查找、插入和删除,平均时间复杂度为 O(1),但最坏情况下可能达到 O(n)。适用于快速查找、插入和删除
树结构在查找、插入和删除操作时间复杂度为 O(log n)。树适合表示层次关系,而哈希表适合快速查找。适用于如文件系统、组织结构等
堆插入和删除操作的时间复杂度为 O(log n),查找最大或最小值的时间复杂度为 O(1),适用于优先队列实现,用于调度和事件处理。
空间复杂度都为:O(n)