八股文面试攻略一:C/C++

C/C++八股文面试核心考点

语言

1、static与const的作用

static修饰静态变量,const修饰常量(只读)。

static(局部):变量声明周期延长、不随函数结束销毁。只初始化一次

static(全局):限制作用域,只在当前文件可见

static(类内):所有对象共享同一份变量、函数

常量指针:指针指向的“值”不能改,但指针的“地址”可以改。const int *p,*靠近的内容不可改

指针常量:指针本身不能改(不能指向别的地方),但指向的内容可以改。int * const p = &a

2、引用与指针的作用以及区别?

引用必须初始化,指针可以不初始化。引用不能改变绑定对象,指针可以改变指向。引用没有 NULL,指针可以为 NULL。引用使用更安全,语法更简洁。引用本质是常量指针

3、如何避免野指针

野指针就是指向非法内存的地址,例如未初始化指针、指向已释放的内存、指向超出作用域的对象、指针越界。

避免方法

  • 指针声明后立刻初始化
  • delete 后立即置为 nullptr。delete p;  p = nullptr; // 避免悬空指针
  • 数组操作必须确保不越界
  • 使用智能指针代替裸指针
  • 指针使用前必须检查是否为 nullptr
  • 尽量使用引用而不是指针
  • 避免多次释放同一块内存
  • 避免返回栈上变量的地址

空指针:指向“无效地址”的指针,它不指向任何有效对象。int* p = nullptr;

4、malloc、free和new、delete区别?malloc申请空间失败怎么办?

malloc和free是标准库函数,而new和delete是C++操作符,可以被重载。

malloc失败后返回NULL,而new失败后抛出异常:std::bad_alloc

5、extern有什么作用

  • 跨文件共享全局变量。
  • C/C++混合编译

并可通过 extern "C" 关闭 C++ 名字修饰以实现与 C 语言链接的兼容。

extern 声明一个变量或函数在其他文件中定义,避免重复定义,同时允许跨文件访问。

它本身不分配内存,只告诉编译器这个符号在别处存在;

6、简述strcpy、sprintf与memcpy的区别?

  • strcpy:拷贝字符串。“/0”结束。用于复制字符串、字符数组操作
  • sprintf:格式化输出到字符串。用于拼接字符串,格式化输出,数字转为字符串
  • memcpy:拷贝内存块。复制结构体、复制数组

7、C++强制类型转换使用场景

有四种强制转

  • static_cast:基础类型之间的转换,最常用、最安全的编译期转换
  • reinterpret_cast:任意类型转换不安全。
  • const_cast:修改对象的“const 性质”。去掉/添加 const 属性
  • dynamic_cast:安全,多态向下转换(父->子)

优先级:static_cast > dynamic_cast > const_cast > reinterpret_cast

8、什么时候使用默认构造函数?

9、什么时候生产默认拷贝构造函数

默认构造函数 = 无参数构造函数。需要先创建对象,而不是立即给构造参数时,就需要默认构造函数。

  • 创建对象数组(最典型)

  • STL 容器默认初始化元素

  • 类成员默认构造

  • new[] 动态数组

  • 操作符 map[key]

  • 框架需要默认构造反射对象

  • 想让类“可直接定义对象”时

10、什么是深拷贝什么是浅拷贝?默认构造函数是哪种拷贝?什么时候使用深拷贝,以及零拷贝

浅拷贝:拷贝指针,不拷贝指向的内容。共享同一块堆内存

深拷贝:开辟一个新内存,将内存放进去。防止数据污染

默认构造函数使用浅拷贝

零拷贝:在网络传输、文件 IO 中,避免在用户态/内核态之间重复复制数据,通过共享内存、DMA、映射技术直接传输数据。减少数据在内核与用户态之间的复制,提升网络传输与大型文件处理性能。

标准库

1、vector的reserve与resize的区别?

reserve:只改变容量(capacity),不改变元素数量,不创建元素。预分配容量,但不创建元素

resize:只改变元素数量(size),会创建或销毁元素。改变 size,创建或删除元素

2、list的底层实现原理

list是双向链表,插入删除O(1),随机访问O(n)。缺点是不能随机访问,遍历慢,空间开销较大。

3、deque的底层实现原理

双端队列。兼具vector与list的部分优点。不是连续内存,插入访问O(1)

std::deque 的底层是分段连续的动态数组:多个固定大小的 buffer 由 map(指针数组)管理。
支持 O(1) 的双端插入,是因为通过增加/减少 buffer 而不需要整体搬移数据。
支持 O(1) 随机访问,是因为 index 可以通过计算找到对应的 buffer 和 offset。

4、什么时候使用vector、list和deque?

大部分情况用vector、双端队列用deque、任意位置插入/删除是 O(1)用list。

5、priority_queue底层实现原理

优先级队列,是一个基于堆的容器适配器,用vector保存数据。维护完全二叉堆,插入删除O(log n),访问O(1)

6、map、set、multimap、multiset底层实现原理,红黑树原理?

这些底层都是红黑树。

红黑树是有序的,五大性质:

  • 每个节点要么黑色要么红色
  • 根是黑色
  • 所有叶子(NIL)节点是黑色
  • 红色节点的子节点必须是黑色
  • 从任意节点到其所有叶子路径上,黑色节点数量相同

红黑树靠五大性质维持平衡,操作通过“旋转 + 变色”修复结构。

7、unordered_map、unordered_set底层,哈希表原理?

底层实现是哈希表+拉链法。插入、查找、删除平均 O(1),最坏 O(n)。无序

哈希表核心思想:通过哈希函数把 key 映射到数组索引,再在该槽位处理冲突。

冲突是不可避免的,方法有:链地址法、

8、迭代器底层实现原理?种类

迭代器 = 封装指针的对象 + 重载运算符

它以统一方式访问容器内部元素,而不暴露容器的底层结构。

迭代器本质是“指针或指针封装”,作为统一访问方式。
vector/deque:指针或类似指针 → 随机访问 iterator。
list:节点指针 → 双向迭代,不支持随机访问。
map/set:红黑树节点指针 → 双向迭代,中序遍历。
unordered_xxx:哈希桶链表 → 前向迭代。
标准迭代器共五种类型:输入、输出、前向、双向、随机访问。

9、迭代器失效?连续存储容器失效?非连续存储容器失效?处理方式

迭代器失效就是迭代器内部指向的元素被移动、删除或其地址变化,导致迭代器不再指向合法数据,使用就会触发未定义行为。

连续存储容器的迭代器失效:因为扩容、插入、删除会导致内存重分配或元素整体移动。

非连续存储容器失效:只有被删除元素的迭代器会失效

技巧:

  • 使用erase和insert返回值
  • 先记录再删除
  • 使用稳定的容器操作

vector: 插入或删除元素后,该位置及其后面的迭代器都会失效;如果重新分配内存,所有迭代器都会失效。 list/forward_list: 只有被删除元素的迭代器会失效。 map/set/multimap/multiset: 只有被删除元素的迭代器会失效。 unordered_map/unordered_set: 插入操作可能导致所有迭代器失效(rehash);删除操作只会导致被删除元素的迭代器失效。

10、STL容器线程安全性

STL 容器本身不是线程安全的,读写混用一定要加锁。

面向对象

1、面向对象三大特征

继承、封装、多态

2、简述多态的实现原理

分为动态多态与静态多态。多态就是同一操作作用于不同对象,可以表现出不同的行为。

动态多态通过虚函数实现(virtual),属于运行时多态,运行时通过查找虚函数表实现动态多态。

静态多态主要通过函数重载、模板函数实现。

虚函数机制:编译器生成一个虚函数表(vtable),对象中多一个隐藏指针(vptr),指向该类的vtable。通过vptr找到vtable,在找到正确的函数地址。因此父类指针调用虚函数时,根据vptr找到实际指向对象类型调用对应函数。动态多态 = vptr + vtable + 虚函数

3、怎么解决菱形继承

通过多重继承导致同一个基类在最终派生类中出现多份,引发成员二义性和对象模型混乱。

首先菱形继承属于一个基类A,B、C继承A,D继承B、C形成的菱形继承。

一般使用虚继承来解决,例如在B、C继承A的地方加virtual。

4、函数重载和重写的区别?

重载(overload):同一个作用域,函数名相同,参数不同。静态多态

重写(override):父类与子类之间,函数一致,父类需要加virtual。动态多态

5、运行时多态实现原理

结合虚函数机制

6、虚函数调用过程

同上

7、继承下构造函数与析构函数执行顺序

执行顺序:父类(构造函数)子类(构造函数)子类(析构函数)父类(析构函数)

8、虚函数表和虚函数指针(vptr)的创建时机

虚函数表(vtable)在编译阶段,vptr在对象构造阶段由构造函数进行初始化。

构造时从基类 vtable 切到派生类,析构时从派生切回基类。

9、虚析构函数的作用?

虚析构函数让“父类指针指向子类对象”时,delete 时能够正确调用子类析构函数,防止资源泄漏。

10、智能指针种类以及使用场景

主要有三种:

  • unique_ptr:独占型智能指针
  • share_ptr:共享型智能指针(引用计数)
  • weak_ptr:弱引用(避免循环引用)

11、C++11常用特性

auto、nullptr 空指针常量、智能指针移动语义、右值引用、时间库、枚举、static_assert、override / final 关键字、STL

C++11 的核心特性包括:
auto、range-for、nullptr、lambda、智能指针、
右值引用与移动语义、initializer_list、
std::thread、chrono、enum class、static_assert、
override/final、constexpr、decltype。

这些特性让 C++ 更安全、更高效、更现代化。

12、动态库与静态库的区别

静态库:.a结尾,window是.lib。

特点:链接时复制进可执行文件、可执行文件体积变大、更新库需重新编译整个程序、加载速度快。

优点:运行时不依赖外部文件,部署简单。启动更快。不会出现“库版本不一致”问题

缺点:可执行文件体积变大、库更新后需要重新编译所有依赖它的程序、占用内存较大

动态库:.so和.dll

特点:链接时不复制库代码,运行时动态加载。多个程序共享同一个库文件、库可以单独更新,不需要重新编译整个程序、加载时有额外开销

优点:节约内存、便于更新

缺点:发布时要带上 .so/.dll,部署更复杂、启动时需动态加载,速度略慢、库版本不匹配会导致“运行错误”

静态库:链接时复制到可执行文件中,不依赖外部库,体积大,更新麻烦。
动态库:运行时加载,不复制,多进程共享,占用内存小,便于更新,但运行依赖库文件。

13、右值引用与左值引用区别?右值引用的意义

右值:是一个将亡值,临时对象

左值:有名字、有地址。

右值引用的目的为了支持移动语义,避免不必要的拷贝,特别是临时对象、返回值优化、容器操作等场景。move。

左值引用绑定左值,右值引用绑定右值。
右值引用的意义是实现移动语义和完美转发,
避免临时对象拷贝,提高性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值