1、什么是虚函数?什么是纯虚函数?
虚函数是在基类中使用 virtual 关键字声明的成员函数,它允许在派生类中重新定义该函数,实现运行,使用 virtual 关键字声明。
纯虚函数是在基类中声明的虚函数,它没有函数体,而是在声明时被初始化为 0。用 virtual 关键字声明,并且在函数声明末尾加上 = 0。
2、基类为什么需要虚析构函数?
当通过基类指针删除派生类对象时,如果基类的析构函数不是虚函数,则只会调用基类的析构函数,而不会调用派生类的析构函数。这可能导致派生类特有的资源没有被正确释放,从而造成内存泄漏。
3、如何初始化const 和 static数据成员?
1. const数据成员初始化
- 在类中,非静态的`const`数据成员必须在构造函数的初始化列表中进行初始化。这是因为`const`对象一旦创建就不能被修改,所以不能先默认初始化再赋值。
- 在这个例子中,`num`是`const`数据成员,通过构造函数的初始化列表,将传入的参数`n`用来初始化`num`。
- 对于静态`const`数据成员,如果是基本数据类型(如`int`、`char`等),可以在类内直接初始化。
- 如果静态`const`数据成员是复杂类型(如自定义类类型),则需要在类外定义(但不需要再次初始化,因为已经在类内初始化了)。
2.静态数据成员必须在类外进行定义和初始化(除了前面提到的静态const
基本数据类型可以在类内初始化)
4、指针和引用的区别
(1)指针:是一个变量,存储另一个变量的内存地址;可以不初始化;可以重新赋值指向其他变量。
(2)引用:是一个变量的别名;必须在声明时初始化;一旦初始化后,不能再引用其他变量。
5、.new 和 malloc 的区别
new: C++ 运算符 malloc: C 语言函数,在 C++ 中仍然可用
new: 返回对象的指针,类型安全 malloc: 返回 void*,需要手动类型转换
new: 抛出 std::bad_alloc 异常 malloc: 返回 NULL
new: 自动计算所需内存大小 malloc: 需要手动指定内存大小
- new: 调用构造函数 malloc: 不调用构造函数
- new: 使用 delete 释放,调用析构函数 malloc: 使用 free 释放,不调用析构函数
- new: 使用 new[],释放时使用 delete[] malloc: 手动计算数组大小,使用 free 释放
- new: 可以被重载 malloc: 是函数,不能被重载
6、内存泄漏怎么产生的?如何避免?
原因:忘记释放内存;异常处理不当;循环引用;不正确的资源管理;指针重新赋值而忘记释放原内存
方法:使用智能指针;使用 try-catch 块和智能指针结合;避免循环引用
7、C++ 的内存分区
1.代码区
2.数据区
2.1 初始化数据段
2.2 未初始化数据段
3. 堆
4. 栈
5. 内存映射区
9、常用的数据结构有哪些?时间复杂度和空间复杂度如何使用
常用的数据结构包括数组、链表、栈、队列、树、哈希表等
1. 数组(Array)
特点:
- 固定大小,连续内存
- 随机访问
复杂度:
- 访问:O(1)
- 搜索:O(n)
- 插入/删除(末尾):O(1)
- 插入/删除(任意位置):O(n)
2. 链表(Linked List)
特点:
- 动态大小,非连续内存
- 顺序访问
复杂度:
- 访问:O(n)
- 搜索:O(n)
- 插入/删除(头部):O(1)
- 插入/删除(任意位置):O(n)
空间复杂度:O(n)
3. 栈(Stack)
特点:
- 后进先出(LIFO)
复杂度:
- 推入/弹出:O(1)
- 访问顶部元素:O(1)
空间复杂度:O(n)
4. 队列(Queue)
特点:
- 先进先出(FIFO)
复杂度:
- 入队/出队:O(1)
- 访问前端/后端元素:O(1)
空间复杂度:O(n)
5. 树(Tree)
5.1 二叉搜索树(BST)
复杂度(平均情况):
- 搜索/插入/删除:O(log n)
- 最坏情况(不平衡树):O(n)
空间复杂度:O(n)
5.2 平衡树(如 AVL 树、红黑树)
复杂度:
- 搜索/插入/删除:O(log n)
空间复杂度:O(n)
6. 哈希表(Hash Table)
复杂度(平均情况):
- 插入/删除/搜索:O(1)
- 最坏情况:O(n)
空间复杂度:O(n)