1. C++中的多态性是如何实现的?
多态性允许使用基类指针或引用调用派生类的函数。在C++中,这主要通过虚函数机制实现。当基类声明虚函数时,派生类可以重写这些函数。编译器会为每个包含虚函数的类生成虚函数表(vtable),对象则包含指向该表的指针。运行时根据对象实际类型调用正确的函数实现。
cpp
class Base {
public:
virtual void show() { cout << "Base\n"; }
};
class Derived : public Base {
public:
void show() override { cout << "Derived\n"; }
};
2. 智能指针的种类及其区别?
智能指针用于自动化内存管理,主要分为三类:
unique_ptr:独占所有权,不可复制但可移动shared_ptr:通过引用计数实现共享所有权weak_ptr:配合shared_ptr使用,不增加引用计数
cpp
auto ptr1 = make_unique<int>(10); // 独占
auto ptr2 = make_shared<int>(20); // 共享
weak_ptr<int> ptr3 = ptr2; // 观察
3. 左值和右值的根本区别?
左值(lvalue)表示有持久存储位置的表达式,右值(rvalue)通常是临时对象或字面量。关键区别在于:
- 左值可被赋值和取地址
- 右值只能出现在赋值右侧
- C++11引入右值引用(
&&)实现移动语义
4. 移动语义解决了什么问题?
移动语义通过转移资源所有权而非深拷贝来优化性能。典型场景:
- 避免大型对象拷贝开销
- 实现高效容器操作
- 使用
std::move显式转换左值为右值
cpp
vector<string> v;
string s = "data";
v.push_back(std::move(s)); // s资源转移后为空
5. const关键字的不同用法?
const的多场景应用:
- 常量变量:
const int MAX = 100; - 常量指针:
const char* p(内容不可变) - 指针常量:
char* const p(指针不可变) - 成员函数:
void func() const(不修改对象状态) - 函数参数:避免意外修改
6. 虚函数表的工作机制?
虚函数表(vtable)是实现动态绑定的核心机制:
- 每个多态类拥有自己的vtable
- vtable存储虚函数地址
- 对象内含
vptr指向其vtable - 调用虚函数时通过
vptr间接寻址
7. 模板元编程的应用场景?
模板元编程(TMP)在编译期执行计算:
- 类型萃取(如
std::is_integral) - 编译期常量计算
- 条件编译优化
- 生成高效专用代码
cpp
template<int N>
struct Factorial {
static const int value = N * Factorial<N-1>::value;
};
template<>
struct Factorial<0> { static const int value = 1; };
8. RAII原则的核心思想?
资源获取即初始化(RAII)是C++核心范式:
- 构造函数获取资源
- 析构函数释放资源
- 确保异常安全
- 智能指针是典型实现
9. STL容器的时间复杂度?
常见容器操作复杂度:
vector:随机访问O(1),插入删除O(n)list:插入删除O(1),访问O(n)map(红黑树):操作O(\\log n)unordered_map:平均O(1),最坏O(n)
10. 如何避免内存泄漏?
关键防御措施:
- 优先使用智能指针
- RAII管理资源
- 避免原始指针所有权模糊
- 使用内存检测工具(Valgrind)
11. 完美转发的实现机制?
std::forward保持参数原始值类别:
- 配合模板参数
T&&使用 - 转发左值/右值属性
- 实现通用引用
cpp
template<typename T>
void wrapper(T&& arg) {
target(std::forward<T>(arg));
}
12. 异常安全保证等级?
四个标准等级:
- 基本保证:不泄露资源,对象保持有效
- 强保证:操作完全成功或回滚
- 不抛保证:承诺不抛出异常
- 无保证:可能遗留非法状态
13. 类型推导规则(auto/decltype)?
类型推导机制:
auto:模板类型推导规则decltype:返回表达式声明类型decltype(auto):保持值类别
cpp
auto x = 10; // int
decltype(x) y = 20; // int
decltype(auto) z = y; // int&
14. 菱形继承问题解决方案?
虚继承解决多重继承路径:
cpp
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // 仅一份A实例
虚基类由最派生类直接构造
15. lambda表达式的实现本质?
Lambda是匿名函数对象:
- 编译器生成唯一类
- 捕获变量变为成员
operator()实现函数体- 捕获方式:[=]值捕获,[&]引用捕获
16. 并发编程的三大问题?
核心挑战:
- 竞态条件:未同步数据访问
- 死锁:相互等待资源
- 活锁:不断尝试但无法进展 解决方案:互斥锁、原子操作、条件变量
17. 静态断言的使用场景?
static_assert编译期断言:
- 验证模板参数
- 检查类型特性
- 确保平台兼容性
cpp
static_assert(sizeof(int)==4, "int must be 4 bytes");
18. 如何实现线程安全容器?
设计要点:
- 细粒度锁(每个桶独立锁)
- 无锁数据结构(CAS操作)
- 写时复制(COW)
- 接口设计避免竞争
19. 三/五/零法则?
特殊成员函数管理规则:
- 三法则:需要自定义析构、拷贝构造、拷贝赋值
- 五法则:增加移动构造、移动赋值
- 零法则:依赖编译器默认实现
20. CRTP模式的应用?
奇异递归模板模式:
cpp
template <typename T>
class Base {
public:
void interface() {
static_cast<T*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
void implementation() { /*...*/ }
};
用于静态多态、代码复用
21. 指针与引用的本质区别?
核心差异:
- 引用必须初始化且不可重绑定
- 指针可为空,支持多级间接
- 引用无独立存储空间
- sizeof引用返回目标大小
22. 纯虚函数的作用?
抽象类定义规范:
- 声明
virtual void func() = 0; - 强制派生类实现
- 不能实例化抽象类
- 可包含非虚成员函数
23. 前置声明适用场景?
减少编译依赖:
- 声明类/结构体:
class MyClass; - 可用于指针/引用类型
- 不能用于定义成员对象
- 头文件中广泛使用
24. 位域的内存布局?
精确控制成员位宽:
cpp
struct S {
unsigned int a : 4; // 4位
unsigned int : 2; // 无名位域
bool b : 1;
};
注意对齐规则和移植性问题
25. 类型擦除的实现技术?
隐藏具体类型:
void*+函数指针(C风格)- 继承+虚函数(多态)
std::function(函数对象)std::any(C++17容器)
26. 协程的核心优势?
轻量级并发单元:
- 用户态调度
- 无上下文切换开销
- 同步式异步编程
- C++20标准支持
cpp
generator<int> seq() {
for(int i=0; ; ++i) co_yield i;
}
27. 如何优化虚函数调用?
性能优化手段:
final类/函数阻止重写- 编译器去虚拟化
- 避免深度继承
- 模板替代运行时多态
28. 结构化绑定用法?
解绑复合类型:
cpp
std::pair<int, string> p{1, "test"};
auto [id, name] = p; // id=1, name="test"
支持数组、元组、自定义类型
29. 常量表达式上下文?
constexpr编译期计算:
- 变量:常量初始化
- 函数:可在编译时执行
- if语句:编译期分支
- C++20新增
consteval
30. 内存对齐控制方法?
对齐控制:
alignas指定对齐值alignof获取对齐要求#pragma pack改变默认对齐- 影响内存访问效率