C++ 经典面试题精选
第一部分:C++ 基础语法
面向对象基础
1. 什么是面向对象编程?C++ 中如何体现?
面向对象编程(OOP)是一种通过对象组织代码的范式。在 C++ 中主要体现在封装、继承和多态上。
class Animal {
public:
virtual void makeSound() = 0; // 纯虚函数
};
class Dog : public Animal {
public:
void makeSound() override {
std::cout << "Woof!" << std::endl;
}
};
2. 类和结构的区别?
主要区别在于默认访问权限:class 默认为 private,而 struct 默认为 public。
class MyClass {
int x; // private
};
struct MyStruct {
int x; // public
};
3. 构造函数的作用和种类?
构造函数用于初始化对象。包括默认构造函数、参数化构造函数、拷贝构造函数以及 C++11 引入的移动构造函数。
class Example {
public:
Example() {} // 默认构造
Example(int x) : val(x) {} // 参数化构造
Example(const Example& other) : val(other.val) {} // 拷贝构造
Example(Example&& other) noexcept : val(other.val) {} // 移动构造
private:
int val;
};
4. 析构函数的作用?什么时候调用?
析构函数用于清理对象资源。当对象生命周期结束、使用 delete 释放动态分配对象或程序正常结束时调用。
class Resource {
public:
Resource() { data = new int[100]; }
~Resource() { delete[] data; } // 释放资源
private:
int* data;
};
5. 什么是拷贝构造函数?为什么要自己实现?
拷贝构造函数用同类型对象初始化新对象。如果类包含指针成员且需要深拷贝,或者有特殊逻辑时,必须手动实现。
class String {
public:
String(const char* str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
}
// 自定义拷贝构造函数(深拷贝)
String(const String& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
~String() { delete[] data; }
private:
char* data;
};
6. 什么是移动构造函数?有什么优势?
移动构造函数(C++11)通过'窃取'资源而非拷贝来提高性能,避免深拷贝开销,特别适用于临时对象和大对象。
class Vector {
public:
// 移动构造函数
Vector(Vector&& other) noexcept
: data(other.data), size(other.size), capacity(other.capacity) {
other.data = nullptr;
other.size = other.capacity = 0;
}
private:
int* data;
size_t size, capacity;
};
7. 初始化列表的作用和优势?
初始化列表直接初始化类成员,效率更高(避免先默认构造再赋值)。对于 const 成员、引用成员及无默认构造函数的成员是必须的。
class Person {
public:
Person(const std::string& name, int age)
: name_(name), age_(age) {
// 初始化列表
// 构造函数体
}
private:
std::string name_;
int age_;
};
8. this 指针的作用?
this 指针指向当前对象,用于区分成员变量和参数,返回当前对象的引用等。
class Student {
public:
Student& setName(const std::string& name) {
this->name = name; // 区分成员变量
return *this; // 返回当前对象引用
}
private:
std::string name;
};
访问控制与封装
9. public、private、protected 的区别?
- public:类内外都可访问。
- private:只有类内和友元可访问。
- protected:类内、派生类、友元可访问。
class Base {
public:
int pub; // 公有成员
protected:
int prot; // 保护成员
private:
int priv; // 私有成员
};
class Derived : public Base {
public:
void access() {
pub = 1; // OK
prot = 2; // OK:派生类可访问
// priv = 3; // Error
}
};
10. 友元函数和友元类的作用?
友元可以访问类的私有和保护成员。友元函数是非成员函数,友元类则是另一个类的所有成员函数都可访问。
class Box {
friend void printBox(const Box& box); // 友元函数
friend class BoxPrinter; // 友元类
private:
int width, height;
};
void printBox(const Box& box) {
std::cout << box.width << "x" << box.height;
}
11. 什么是封装?为什么重要?
封装将数据和操作数据的方法绑定在一起,隐藏内部实现。这有助于数据保护、接口清晰化和易于维护。
class BankAccount {
public:
void deposit(double amount) { balance += amount; }
bool withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
return true;
}
return false;
}
double getBalance() const { return balance; }
private:
double balance = 0.0;
};
12. const 成员函数的作用?
承诺不修改对象状态,可以被 const 对象调用,不能修改非 mutable 成员变量,提高安全性和可读性。
class Rectangle {
public:
double getArea() const {
return width * height; // 只读操作
}
void setWidth(double w) { width = w; }
private:
double width, height;
};
13. mutable 关键字的作用?
允许在 const 成员函数中修改特定成员变量,常用于缓存、计数器等逻辑上保持 const 但需修改的场景。
class Calculator {
public:
int getResult() const {
cache_count++; // mutable 成员可在 const 函数中修改
return compute();
}
private:
mutable int cache_count = 0;
int compute() const { return 42; }
};
继承与多态
14. 继承的方式有哪些?区别是什么?
C++ 有三种继承方式:public(保持访问级别)、protected(基类 public/protected 变为 protected)、private(基类 public/protected 变为 private)。
class Base {
public: int pub;
protected: int prot;
private: int priv;
};
class PublicDerived : public Base {}; // pub 保持 public
class PrivateDerived : private Base {}; // pub 和 prot 都变成 private
15. 什么是虚函数?作用是什么?
虚函数实现运行时多态,允许通过基类指针调用派生类的实现。需要 virtual 关键字声明,通过虚函数表实现。
class Shape {
public:
virtual double getArea() const = 0; // 纯虚函数
virtual void draw() const {
std::cout << "Drawing shape" << std::endl;
}
};
class Circle : public Shape {
public:
double getArea() const override {
return 3.14 * radius * radius;
}
void draw() const override {
std::cout << "Drawing circle" << std::endl;
}
private:
double radius;
};
16. 纯虚函数和抽象类的概念?
- 纯虚函数:没有实现的虚函数,
= 0表示。 - 抽象类:包含纯虚函数的类,不能实例化。
class Animal {
public:
virtual void makeSound() = 0; // 纯虚函数
virtual void eat() {
std::cout << "Eating" << std::endl;
}
};
// Animal animal; // Error:不能实例化抽象类
class Dog : public Animal {
public:
void makeSound() override {
std::cout << "Woof!" << std::endl;
}
};
17. 什么是虚函数表 (vtable)?
虚函数表是实现多态的机制。每个有虚函数的类都有一个虚函数表,对象中包含指向它的指针(vptr),表中存储虚函数的地址。
// 编译器为这个类生成虚函数表
class Base {
public:
virtual void func1() {}
virtual void func2() {}
};
// 内存布局:[vptr][成员变量]
// vptr 指向虚函数表:[&Base::func1, &Base::func2]
18. override 关键字的作用?
明确表示重写基类虚函数。编译器会检查是否真的重写了基类函数,防止拼写错误或参数不匹配,提高可读性。
class Base {
public:
virtual void process(int value) {}
};
class Derived : public Base {
public:
void process(int value) override {
// 明确表示重写
// 如果基类没有对应的虚函数,编译器会报错
}
};
19. final 关键字的作用?
阻止继承或重写。修饰类表示不能被继承,修饰虚函数表示不能被重写。
class FinalClass final {
public:
virtual void method() final {
// 不能被重写
}
};
// class Derived : public FinalClass {} // Error:不能继承 final 类
20. 多重继承的问题和解决方案?
问题包括菱形继承导致的二义性和数据冗余。解决方案是使用虚继承或明确作用域限定。
class Animal {
public:
void eat() {
std::cout << "Eating" << std::endl;
}
};
class Mammal : virtual public Animal {};
class Bird : virtual public Animal {};
class Bat : public Mammal, public Bird {
public:
void test() {
eat(); // OK:没有二义性
}
};
运算符重载
21. 运算符重载的规则和限制?
不能重载的运算符包括 ., ::, sizeof, typeid, .*, ?:。必须重载为成员函数的有 =, (), [], ->。不能改变优先级和结合性。
class Complex {
public:
// 重载为成员函数
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 重载为友元函数(二元运算符推荐)
friend Complex operator*(const Complex& c1, const Complex& c2);
private:
double real, imag;
};
22. 前置 ++ 和后置 ++ 的区别?
前置 ++ 无参数,返回引用;后置 ++ 带 int 参数(仅用于区分),返回值。
class Counter {
public:
// 前置 ++
Counter& operator++() {
++value;
return *this;
}
// 后置 ++
Counter operator++(int) {
Counter temp = *this;
++value;
return temp;
}
private:
int value = 0;
};
23. 赋值运算符重载的注意事项?
检查自赋值,返回当前对象的引用,正确处理资源,考虑异常安全。
class String {
public:
String& operator=(const String& other) {
if (this != &other) {
delete[] data;
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
return *this;
}
private:
char* data;
};
24. 下标运算符 [] 的重载?
通常提供 const 和非 const 两个版本,应进行边界检查,返回引用以支持修改。
class Array {
public:
int& operator[](size_t index) {
if (index >= size) throw std::out_of_range("Index out of range");
return data[index];
}
const int& operator[](size_t index) const {
if (index >= size) throw std::out_of_range("Index out of range");
return data[index];
}
private:
int* data;
size_t size;
};
25. 函数调用运算符 () 的重载?
使对象可以像函数一样调用,创建函数对象(functor),支持任意参数列表,常用于 STL 算法。
class Adder {
public:
int operator()(int a, int b) const {
return a + b;
}
};
class Multiplier {
private:
int factor;
public:
Multiplier(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
// 使用 Adder add; std::cout << add(3, 4) << std::endl;
// Multiplier times3(3); std::cout << times3(5) << std::endl;
第二部分:C++ 核心特性
模板编程
26. 什么是函数模板?
允许编写泛型函数,支持多种数据类型。使用 template 关键字声明,类型参数用 typename 或 class。
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// 使用
int i = max(3, 7); // T = int
double d = max(3.14, 2.71); // T = double
27. 什么是类模板?
定义泛型类,支持多种数据类型。成员函数在类外定义时需要模板声明,支持非类型模板参数和特化。
template<typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(const T& elem) {
elements.push_back(elem);
}
T pop() {
if (elements.empty()) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
T elem = elements.back();
elements.pop_back();
return elem;
}
};
// 使用
Stack<int> intStack;
Stack<std::string> stringStack;
28. 模板特化的概念和用法?
为特定类型提供定制实现。分为完全特化(所有参数指定)和部分特化(部分参数指定,仅类模板)。
// 通用模板
template<typename T>
class Calculator {
public:
T add(T a, T b) { return a + b; }
};
// 完全特化
template<>
class Calculator<const char*> {
public:
const char* add(const char* a, const char* b) {
char* result = new char[strlen(a) + strlen(b) + 1];
strcpy(result, a);
strcat(result, b);
return result;
}
};
// 部分特化(仅类模板)
template<typename T>
class Calculator<T*> {
public:
T add(T* a, T* b) { return *a + *b; }
};
29. 模板参数的类型?
包括类型参数(typename T)、非类型参数(int size, bool flag)和模板模板参数(template class Container)。
template<typename T, int SIZE, bool DEBUG = false>
class Buffer {
private:
T data[SIZE];
public:
void set(int index, const T& value) {
if (DEBUG) std::cout << "Setting index " << index << std::endl;
data[index] = value;
}
};
// 模板模板参数
template<typename T, template<typename, typename> class Container>
class Adapter {
private:
Container<T, std::allocator<T>> container;
public:
void add(const T& item) {
container.push_back(item);
}
};
30. SFINAE 是什么?
替换失败不是错误(Substitution Failure Is Not An Error)。模板参数替换失败时不会编译错误,而是寻找其他重载。常用于模板元编程和类型萃取。
template<typename T>
typename T::value_type test(typename T::iterator*);
template<typename>
int test(...);
template<typename T>
auto hasIterator(T t) -> decltype(test<T>(nullptr)) {
return true; // 有 iterator
}
template<typename T>
int hasIterator(...) {
return false; // 没有 iterator
}
31. 模板实例化的过程?
- 编译期遇到模板使用时生成具体代码。
- 两阶段查找:模板定义时和实例化时。
- 隐式实例化:使用时自动生成。
- 显式实例化:明确指定类型。
// 模板定义
template<typename T>
T square(T x) {
return x * x;
}
// 隐式实例化
int result1 = square(5); // 实例化 square<int>
double result2 = square(3.14); // 实例化 square<double>
// 显式实例化
template int square<int>(int);
32. 模板的优缺点?
优点:代码复用、类型安全、性能与手写代码相同。缺点:编译时间长、代码膨胀、调试困难、老旧编译器支持有限。
STL 容器
33. vector 的特点和适用场景?
动态数组,连续内存,随机访问,自动扩容。访问 O(1),插入删除 O(n)。适用于频繁随机访问、尾部插入。
std::vector<int> vec;
vec.push_back(1); // 尾部插入
vec[0] = 10; // 随机访问
vec.size(); // 元素个数
vec.capacity(); // 容量
vec.reserve(100); // 预分配容量
34. list 的特点和适用场景?
双向链表,非连续内存,双向遍历,任意位置插入删除。访问 O(n),插入删除 O(1)。适用于频繁插入删除、不需要随机访问。
std::list<int> lst;
lst.push_front(1); // 头部插入
lst.push_back(2); // 尾部插入
auto it = lst.begin();
lst.insert(it, 3); // 任意位置插入
lst.erase(it); // 删除元素
35. map 和 unordered_map 的区别?
| 特性 | map | unordered_map |
|---|---|---|
| 底层结构 | 红黑树 | 哈希表 |
| 有序性 | 有序 | 无序 |
| 查找复杂度 | O(log n) | O(1) 平均 |
| 插入复杂度 | O(log n) | O(1) 平均 |
| 内存占用 | 较小 | 较大 |
// map - 有序
std::map<std::string, int> ordered_map;
ordered_map["apple"] = 1;
// unordered_map - 无序但更快
std::unordered_map<std::string, int> unordered_map;
unordered_map["apple"] = 1;
36. set 和 multiset 的区别?
- set:元素唯一、自动排序。
- multiset:元素可重复、自动排序。
std::set<int> s;
s.insert(1);
s.insert(1); // 重复插入失败
// s = {1}
std::multiset<int> ms;
ms.insert(1);
ms.insert(1); // 重复插入成功
// ms = {1, 1}
37. deque 的特点?
双端队列,两端插入删除高效,非严格连续内存。两端操作 O(1),中间操作 O(n)。适用于需要在两端频繁操作。
std::deque<int> dq;
dq.push_front(1); // 头部插入
dq.push_back(2); // 尾部插入
dq.pop_front(); // 头部删除
dq.pop_back(); // 尾部删除
38. 容器的选择策略?
- vector:需要随机访问、尾部操作。
- list:频繁中间插入删除。
- deque:两端频繁操作。
- map:需要键值对、有序。
- unordered_map:需要键值对、无序但快速。
- set:唯一元素、有序。
- priority_queue:需要优先级队列。
39. 容器的 emplace 系列函数?
直接构造元素,避免临时对象。包括 emplace(指定位置)、emplace_back(尾部)、emplace_front(头部)。
std::vector<std::pair<int, std::string>> vec;
// 传统方式:构造临时对象
vec.push_back(std::make_pair(1, "hello"));
// emplace 方式:直接构造
vec.emplace_back(1, "hello"); // 更高效
40. 容器的空间配置器?
管理内存分配。默认 allocator 使用 new/delete,可自定义特殊需求或内存池以提高效率。
template<typename T>
class MyAllocator {
public:
using value_type = T;
T* allocate(size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, size_t) {
::operator delete(p);
}
};
// 使用自定义分配器
std::vector<int, MyAllocator<int>> vec;
STL 算法
41. sort 算法的使用和注意事项?
快速排序实现,平均 O(n log n),最坏 O(n²),不稳定排序。可提供自定义比较函数。
std::vector<int> vec = {3, 1, 4, 1, 5};
// 默认升序
std::sort(vec.begin(), vec.end());
// 降序
std::sort(vec.begin(), vec.end(), std::greater<int>());
// 自定义比较
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a % 10 < b % 10; // 按个位数排序
});
42. find 和 binary_find 的区别?
- find:线性搜索,O(n),适用于未排序序列。
- binary_search:二分搜索,O(log n),需要有序序列。
std::vector<int> vec = {1, 3, 5, 7, 9};
// find - 线性搜索
auto it1 = std::find(vec.begin(), vec.end(), 5);
// binary_search - 二分搜索(需要先排序)
bool found = std::binary_search(vec.begin(), vec.end(), 5);
// lower_bound/upper_bound - 找范围
auto lower = std::lower_bound(vec.begin(), vec.end(), 5);
auto upper = std::upper_bound(vec.begin(), vec.end(), 5);
43. for_each 和范围 for 循环的区别?
- for_each:算法函数,可配合函数对象。
- 范围 for:语言特性,更简洁。
std::vector<int> vec = {1, 2, 3, 4, 5};
// for_each
std::for_each(vec.begin(), vec.end(), [](int& x) {
x *= 2;
});
// 范围 for 循环
for (int& x : vec) {
x *= 2;
}
44. transform 算法的使用?
对序列进行变换。一元变换对每个元素应用函数,二元变换对两个序列对应元素应用函数。
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dest(src.size());
// 一元变换
std::transform(src.begin(), src.end(), dest.begin(), [](int x) {
return x * x;
});
// 二元变换
std::vector<int> src2 = {5, 4, 3, 2, 1};
std::vector<int> dest2(src.size());
std::transform(src.begin(), src.end(), src2.begin(), dest2.begin(), [](int a, int b) {
return a + b;
});
45. remove 和 erase 的区别?
- remove:将不需要的元素移到末尾,返回新逻辑终点。
- erase:真正删除元素。
std::vector<int> vec = {1, 2, 3, 4, 5, 3, 6};
// remove-erase 惯用法
auto new_end = std::remove(vec.begin(), vec.end(), 3); // 移动 3 到末尾
vec.erase(new_end, vec.end()); // 真正删除
// 一行完成
vec.erase(std::remove(vec.begin(), vec.end(), 3), vec.end());
智能指针
46. unique_ptr 的特点和使用?
独占所有权的智能指针。不能复制,只能移动。自动释放,轻量级,开销接近原始指针。
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
// std::unique_ptr<int> ptr2 = ptr1; // Error:不能复制
std::unique_ptr<int> ptr2 = std::move(ptr1); // OK:移动
// 自定义删除器
std::unique_ptr<FILE, decltype(&fclose)> file(fopen("test.txt", "r"), fclose);
47. shared_ptr 的特点和使用?
共享所有权的智能指针。引用计数,多个指针共享对象。引用计数操作原子线程安全。注意循环引用可能导致泄漏。
std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
std::shared_ptr<int> ptr2 = ptr1; // 引用计数 +1
std::cout << ptr1.use_count() << std::endl; // 输出 2
ptr2.reset(); // 引用计数 -1
std::cout << ptr1.use_count() << std::endl; // 输出 1
48. weak_ptr 的作用?
解决 shared_ptr 的循环引用问题。弱引用不影响引用计数,作为观察者观察 shared_ptr 管理的对象,通过 lock() 获取 shared_ptr。
class Node {
public:
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 使用 weak_ptr 避免循环引用
};
std::shared_ptr<Node> node1 = std::make_shared<Node>();
std::shared_ptr<Node> node2 = std::make_shared<Node>();
node1->next = node2;
node2->prev = node1; // weak_ptr 不增加引用计数
49. make_shared 和 make_unique 的优势?
异常安全,避免内存泄漏。性能更好,一次内存分配。代码简洁易读。
// 传统方式
std::shared_ptr<int> ptr(new int(42)); // 两次分配:对象 + 控制块
// make_shared 方式
auto ptr = std::make_shared<int>(42); // 一次分配:对象 + 控制块一起
50. 智能指针的选择策略?
- unique_ptr:独占所有权,最轻量。
- shared_ptr:需要共享所有权。
- weak_ptr:解决循环引用,观察者模式。
- 原始指针:不拥有所有权,仅观察。
// 选择指南
class Owner {
std::unique_ptr<Resource> resource; // 独占资源
};
class Observer {
std::weak_ptr<Subject> subject; // 观察主题
};
class Collaborator {
std::shared_ptr<Data> data; // 协作数据
};
第三部分:内存管理与性能
内存管理
51. new 和 malloc 的区别?
| 特性 | new | malloc |
|---|---|---|
| 类型安全 | 安全 | 不安全 |
| 构造函数 | 调用 | 不调用 |
| 析构函数 | delete 调用 | free 不调用 |
| 大小计算 | 自动 | 手动 |
| 失败处理 | 抛出异常 | 返回 NULL |
| 重载 | 可重载 | 不可重载 |
// new - 类型安全,调用构造函数
MyClass* obj1 = new MyClass();
// malloc - 不安全,不调用构造函数
MyClass* obj2 = (MyClass*)malloc(sizeof(MyClass));
// 对应的释放
delete obj1; // 调用析构函数
free(obj2); // 不调用析构函数
52. delete 和 free 的区别?
- delete:调用析构函数,释放 new 分配的内存。
- free:不调用析构函数,释放 malloc 分配的内存。
- delete[]:调用数组析构函数。
- 混用:new 配 delete,malloc 配 free。
// 正确配对
int* p1 = new int;
delete p1;
int* p2 = new int[10];
delete[] p2;
int* p3 = (int*)malloc(sizeof(int));
free(p3);
// 错误配对 - 未定义行为
int* p4 = new int;
free(p4); // 错误
53. 对象的内存布局?
包含虚函数表指针(有虚函数时)、成员变量(按声明顺序)、内存对齐填充、基类成员在前。
class Base {
public:
virtual void func() {}
int a;
char b;
};
class Derived : public Base {
public:
void func() override {}
int c;
};
// Derived 内存布局:
// [vptr][a][padding][b][padding][c]
54. 内存对齐的概念和作用?
数据存储在特定地址边界,提高 CPU 访问效率。规则是按成员最大对齐值对齐,可使用 alignas 控制。
struct Example {
char a; // 1 字节
// 3 字节填充
int b; // 4 字节对齐
char c; // 1 字节
// 3 字节填充
}; // 总大小 12 字节
// 控制对齐
struct alignas(16) Aligned {
int data[4];
}; // 强制 16 字节对齐
55. RAII 是什么?
资源获取即初始化(Resource Acquisition Is Initialization)。构造函数获取资源,析构函数释放资源,保证异常安全。智能指针是其典型应用。
class FileHandler {
public:
FileHandler(const char* filename) {
file = fopen(filename, "r");
if (!file) throw std::runtime_error("Cannot open file");
}
~FileHandler() {
if (file) fclose(file); // 自动释放
}
FILE* get() { return file; }
private:
FILE* file;
};
// 使用
{
FileHandler fh("test.txt"); // 获取资源
// 使用文件...
} // 自动释放资源
56. 对象的生命周期?
阶段包括分配、初始化、使用、销毁、释放。不同存储期的对象(全局、静态、局部、动态)生命周期不同。
class Lifecycle {
public:
Lifecycle() { std::cout << "构造" << std::endl; }
~Lifecycle() { std::cout << "析构" << std::endl; }
};
// 不同存储期的对象
Lifecycle global_obj; // 静态存储期,程序结束时销毁
void func() {
static Lifecycle static_obj; // 静态存储期,程序结束时销毁
Lifecycle local_obj; // 自动存储期,函数结束时销毁
auto* dynamic_obj = new Lifecycle(); // 动态存储期,delete 时销毁
delete dynamic_obj;
}
57. 内存泄漏的检测和预防?
检测工具:Valgrind、AddressSanitizer。预防策略:RAII、智能指针、代码审查。
// 使用智能指针预防泄漏
class ResourceManager {
public:
void addResource(std::shared_ptr<Resource> resource) {
resources.push_back(resource);
}
private:
std::vector<std::shared_ptr<Resource>> resources;
};
58. 自定义内存管理?
场景包括性能优化(减少分配开销)、特殊需求(固定大小块、内存池)、调试目的(跟踪内存使用)。
class MemoryPool {
private:
struct Block {
Block* next;
};
Block* freeList = nullptr;
size_t blockSize;
std::vector<void*> chunks;
public:
MemoryPool(size_t size) : blockSize(size) {}
void* allocate() {
if (!freeList) {
char* chunk = new char[blockSize * 100];
chunks.push_back(chunk);
for (int i = 0; i < 100; ++i) {
Block* block = reinterpret_cast<Block*>(chunk + i * blockSize);
block->next = freeList;
freeList = block;
}
}
Block* result = freeList;
freeList = freeList->next;
return result;
}
void deallocate(void* ptr) {
Block* block = static_cast<Block*>(ptr);
block->next = freeList;
freeList = block;
}
};
异常处理
59. C++ 异常处理机制?
三要素:try(可能抛出异常的代码块)、catch(捕获并处理异常)、throw(抛出异常)。
try {
if (error) {
throw std::runtime_error("Something went wrong");
}
} catch (const std::runtime_error& e) {
std::cout << "捕获异常:" << e.what() << std::endl;
} catch (...) {
std::cout << "捕获未知异常" << std::endl;
}
60. 异常安全的级别?
- 基本保证:无资源泄漏,对象状态有效。
- 强保证:操作成功或回滚到原状态。
- 不抛异常保证:noexcept,绝不抛出异常。
// 基本保证
void basicGuarantee() {
auto* ptr = new int[100];
try {
process(ptr);
} catch (...) {
delete[] ptr;
throw;
}
delete[] ptr;
}
// 强保证
void strongGuarantee(std::vector<int>& vec) {
std::vector<int> backup = vec;
try {
modify(vec);
} catch (...) {
vec = backup;
throw;
}
}
// 不抛异常保证
void noThrowGuarantee() noexcept {
// 绝不抛出异常的代码
}
61. noexcept 的作用?
指定函数不抛出异常。优化性能,抛出异常时直接 terminate。移动操作通常标记 noexcept。
// noexcept 函数
void safeFunction() noexcept {
// 如果抛出异常,程序会调用 terminate
}
// 条件 noexcept
template<typename T>
void moveIfNoexcept(T& src, T& dest) noexcept(noexcept(T(move(src)))) {
dest = std::move(src);
}
// 移动构造函数通常标记 noexcept
class MyClass {
public:
MyClass(MyClass&& other) noexcept
: data(std::move(other.data)) {}
private:
std::vector<int> data;
};
62. 异常和错误码的比较?
| 特性 | 异常 | 错误码 |
|---|---|---|
| 性能 | 较高开销 | 低开销 |
| 可读性 | 清晰分离 | 混合逻辑 |
| 强制性 | 强制处理 | 可忽略 |
| 信息量 | 丰富 | 有限 |
| 适用场景 | 异常情况 | 预期错误 |
// 异常方式
int divide(int a, int b) {
if (b == 0) {
throw std::invalid_argument("Division by zero");
}
return a / b;
}
// 错误码方式
enum ErrorCode { SUCCESS, DIVISION_BY_ZERO };
ErrorCode divide(int a, int b, int& result) {
if (b == 0) {
return DIVISION_BY_ZERO;
}
result = a / b;
return SUCCESS;
}
63. RAII 在异常处理中的作用?
确保异常安全。自动清理,资源管理(智能指针、文件句柄等),异常传播时资源正确释放。
class ExceptionSafe {
public:
ExceptionSafe() {
resource1 = acquireResource1();
resource2 = acquireResource2();
// 可能抛出异常
// 如果 resource2 获取失败,resource1 会自动释放
}
~ExceptionSafe() {
releaseResource2(resource2);
releaseResource1(resource1);
}
private:
Resource* resource1;
Resource* resource2;
};
性能优化
64. 移动语义的概念和优势?
转移资源而非拷贝。避免深拷贝,提高大对象性能。右值引用绑定到临时对象。
class String {
public:
// 拷贝构造 - 深拷贝
String(const String& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
}
// 移动构造 - 转移资源
String(String&& other) noexcept
: data(other.data) {
other.data = nullptr; // 置空原对象
}
private:
char* data;
};
// 使用
String s1 = "Hello";
String s2 = s1; // 拷贝构造
String s3 = std::move(s1); // 移动构造
String s4 = String("World"); // 移动构造(临时对象)
65. 完美转发的概念?
保持参数的值类别。转发引用 template T&&,forward 函数保持左值/右值特性,通用引用可绑定左值和右值。
template<typename T>
void wrapper(T&& arg) {
func(std::forward<T>(arg));
}
void func(int& x) { std::cout << "左值引用" << std::endl; }
void func(int&& x) { std::cout << "右值引用" << std::endl; }
int a = 10;
wrapper(a); // 转发为左值引用
wrapper(10); // 转发为右值引用
66. 返回值优化 (RVO)?
编译器优化,避免不必要的拷贝。NRVO 命名返回值优化。条件通常是返回局部对象。
// RVO - 编译器可能优化掉拷贝
String createString() {
String result("Hello");
return result; // 可能直接构造在调用者空间
}
// 更明确的写法
String createString() {
return String("Hello"); // 更容易被优化
}
// 使用
String s = createString(); // 可能没有拷贝操作
67. 编译期优化的技巧?
constexpr 编译期计算,const 常量传播,inline 内联函数,模板元编程编译期计算。
// constexpr - 编译期计算
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(5); // 编译期计算
// 模板元编程
template<int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template<>
struct Factorial<0> {
static const int value = 1;
};
constexpr int fact5 = Factorial<5>::value; // 编译期计算
68. 缓存友好的编程?
遵循局部性原理(时间和空间局部性),数据结构采用连续内存访问,循环优化提高缓存命中率。
// 缓存不友好 - 跳跃访问
void badAccess(int matrix[1000][1000]) {
for (int j = 0; j < 1000; ++j) {
for (int i = 0; i < 1000; ++i) {
matrix[i][j]++; // 列优先访问
}
}
}
// 缓存友好 - 连续访问
void goodAccess(int matrix[1000][1000]) {
for (int i = 0; i < 1000; ++i) {
for (int j = 0; j < 1000; ++j) {
matrix[i][j]++; // 行优先访问
}
}
}
69. 预分配和容量管理?
reserve 预分配容量,resize 改变大小并初始化,shrink_to_fit 释放多余容量。避免重复分配。
std::vector<int> vec;
vec.reserve(1000); // 预分配 1000 个元素的空间
for (int i = 0; i < 1000; ++i) {
vec.push_back(i); // 不会重新分配
}
vec.resize(2000); // 扩展到 2000,新元素初始化为 0
vec.shrink_to_fit(); // 释放未使用的容量
70. 性能分析工具?
常用工具:gprof、perf、Valgrind、Intel VTune。
# 编译时添加调试信息
g++ -g -pg program.cpp -o program
# 运行程序生成 gmon.out
./program
# 分析性能数据
gprof program gmon.out > analysis.txt
# 使用 perf 分析
perf record ./program
perf report
第四部分:高级特性
多线程编程
71. C++11 线程的创建和管理?
支持 std::thread,join() 等待结束,detach() 分离,hardware_concurrency() 获取硬件并发数。
#include <thread>
#include <iostream>
void workerFunction(int id) {
std::cout << "Worker " << id << " is running" << std::endl;
}
int main() {
std::thread t1(workerFunction, 1);
std::thread t2(workerFunction, 2);
t1.join();
t2.join();
unsigned int cores = std::thread::hardware_concurrency();
std::cout << "CPU cores: " << cores << std::endl;
return 0;
}
72. 互斥锁的使用?
保护共享资源。std::mutex 基本锁,std::lock_guard RAII 管理,std::unique_lock 灵活管理,std::recursive_mutex 可重入锁。
#include <mutex>
#include <thread>
#include <vector>
class Counter {
private:
int value = 0;
std::mutex mtx;
public:
void increment() {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁解锁
++value;
}
int getValue() {
std::unique_lock<std::mutex> lock(mtx); // 手动控制
int result = value;
lock.unlock(); // 提前解锁
return result;
}
};
void worker(Counter& counter) {
for (int i = 0; i < 1000; ++i) {
counter.increment();
}
}
int main() {
Counter counter;
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(worker, std::ref(counter));
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final value: " << counter.getValue() << std::endl;
return 0;
}
73. 条件变量的使用?
实现线程同步。std::condition_variable,wait() 等待条件满足,notify_one() 唤醒一个,notify_all() 唤醒所有。
#include <condition_variable>
#include <queue>
#include <thread>
template<typename T>
class ThreadSafeQueue {
private:
std::queue<T> queue;
std::mutex mtx;
std::condition_variable cv;
bool done = false;
public:
void push(const T& item) {
{
std::lock_guard<std::mutex> lock(mtx);
queue.push(item);
}
cv.notify_one(); // 通知一个等待的线程
}
bool pop(T& item) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return !queue.empty() || done; });
if (queue.empty() && done) {
return false;
}
item = queue.front();
queue.pop();
return true;
}
void setDone() {
{
std::lock_guard<std::mutex> lock(mtx);
done = true;
}
cv.notify_all(); // 通知所有等待的线程
}
};
74. 原子操作的使用?
无锁编程。std::atomic,memory_order 内存序,CAS 比较并交换。
#include <atomic>
#include <thread>
#include <vector>
class AtomicCounter {
private:
std::atomic<int> value{0};
public:
void increment() {
value.fetch_add(1, std::memory_order_relaxed);
}
int getValue() const {
return value.load(std::memory_order_acquire);
}
bool compareAndSwap(int expected, int desired) {
return value.compare_exchange_weak(expected, desired);
}
};
// 无锁栈节点
template<typename T>
struct LockFreeNode {
T data;
std::atomic<LockFreeNode*> next;
};
template<typename T>
class LockFreeStack {
private:
std::atomic<LockFreeNode<T>*> head{nullptr};
public:
void push(const T& item) {
LockFreeNode<T>* newNode = new LockFreeNode<T>{item, head.load()};
while (!head.compare_exchange_weak(newNode->next, newNode)) {
// 重试
}
}
};
75. 线程安全的单例模式?
Meyers 单例(C++11 保证线程安全),双重检查锁定,std::call_once。
// Meyers 单例 - C++11 线程安全
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // C++11 保证线程安全
return instance;
}
void doSomething() {
std::cout << "Doing something" << std::endl;
}
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
// 使用 std::call_once
class CallOnceSingleton {
private:
static std::once_flag flag;
static CallOnceSingleton* instance;
CallOnceSingleton() = default;
public:
static CallOnceSingleton& getInstance() {
std::call_once(flag, []() {
instance = new CallOnceSingleton();
});
return *instance;
}
};
std::once_flag CallOnceSingleton::flag;
CallOnceSingleton* CallOnceSingleton::instance = nullptr;
76. future 和 promise 的使用?
异步编程。std::future 获取结果,std::promise 设置结果,std::async 异步执行,std::packaged_task 包装可调用对象。
#include <future>
#include <iostream>
#include <chrono>
// 使用 std::async
int asyncFunction() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 42;
}
void asyncExample() {
std::future<int> future = std::async(std::launch::async, asyncFunction);
std::cout << "Doing other work..." << std::endl;
int result = future.get();
std::cout << "Result: " << result << std::endl;
}
// 使用 promise
void promiseExample() {
std::promise<int> promise;
std::future<int> future = promise.get_future();
std::thread producer([&promise]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
promise.set_value(100);
});
std::thread consumer([&future]() {
int value = future.get();
std::cout << "Received: " << value << std::endl;
});
producer.join();
consumer.join();
}
// 使用 packaged_task
void packagedTaskExample() {
std::packaged_task<int()> task([]() { return 123; });
std::future<int> future = task.get_future();
std::thread t(std::move(task));
t.detach();
int result = future.get();
std::cout << "Packaged task result: " << result << std::endl;
}
77. 线程池的实现?
管理线程资源。固定数量线程,任务队列缓存待执行任务,工作线程从队列取任务执行。
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>
class ThreadPool {
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop = false;
public:
ThreadPool(size_t threads) {
for (size_t i = 0; i < threads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock, [this] { return stop || !tasks.empty(); });
if (stop && tasks.empty()) {
return;
}
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
}
template<typename F, typename... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> result = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
if (stop) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace([task] { (*task)(); });
}
condition.notify_one();
return result;
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : workers) {
worker.join();
}
}
};
// 使用示例
void threadPoolExample() {
ThreadPool pool(4);
std::vector<std::future<int>> results;
for (int i = 0; i < 8; ++i) {
results.emplace_back(pool.enqueue([i]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
return i * i;
}));
}
for (auto&& result : results) {
std::cout << result.get() << std::endl;
}
}
现代 C++ 特性
78. auto 关键字的使用?
自动类型推导。简化代码,减少冗长类型名,简化迭代器,C++14 支持函数返回值推导。注意可能降低可读性。
#include <vector>
#include <map>
#include <string>
void autoExample() {
// 基本使用
auto i = 42; // int
auto d = 3.14; // double
auto s = "hello"; // const char*
// 容器迭代器
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 范围 for 循环
for (auto& item : vec) {
item *= 2; // 修改元素
}
// 复杂类型
std::map<std::string, std::vector<int>> complex_map;
auto& map_ref = complex_map; // 引用
// C++14 函数返回值推导
auto add = [](auto a, auto b) { return a + b; };
auto result = add(3, 4); // int
auto result2 = add(3.14, 2.71); // double
}
79. Lambda 表达式的使用?
匿名函数。语法 [捕获] -> 返回类型 { 函数体 }。捕获方式包括值、引用、混合。应用于 STL 算法、回调函数。
#include <algorithm>
#include <vector>
#include <iostream>
void lambdaExample() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 基本 lambda
auto square = [](int x) { return x * x; };
std::cout << "5 squared: " << square(5) << std::endl;
// 捕获外部变量
int factor = 2;
auto multiply = [factor](int x) { return x * factor; };
std::cout << "5 * 2: " << multiply(5) << std::endl;
// 引用捕获
int sum = 0;
std::for_each(numbers.begin(), numbers.end(), [&sum](int x) {
sum += x;
});
std::cout << "Sum: " << sum << std::endl;
// 混合捕获
int threshold = 5;
int count = 0;
std::for_each(numbers.begin(), numbers.end(), [threshold, &count](int x) {
if (x > threshold) count++;
});
std::cout << "Numbers > 5: " << count << std::endl;
// 可变 lambda
int accumulator = 0;
auto mutableLambda = [accumulator](int x) mutable {
accumulator += x;
return accumulator;
};
std::cout << mutableLambda(10) << std::endl; // 10
std::cout << mutableLambda(20) << std::endl; // 30
// 泛型 lambda (C++14)
auto genericLambda = [](auto a, auto b) { return a + b; };
std::cout << genericLambda(3, 4) << std::endl; // 7
std::cout << genericLambda(3.14, 2.71) << std::endl; // 5.85
}
80. constexpr 的使用?
编译期常量。提高运行时性能。函数和变量都可以标记。限制只能使用编译期可用操作。
// constexpr 变量
constexpr int MAX_SIZE = 100;
constexpr double PI = 3.14159265359;
// constexpr 函数
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int fact5 = factorial(5); // 编译期计算
// constexpr 类
class Point {
private:
double x_, y_;
public:
constexpr Point(double x, double y) : x_(x), y_(y) {}
constexpr double x() const { return x_; }
constexpr double y() const { return y_; }
constexpr double distance() const { return x_ * x_ + y_ * y_; }
};
constexpr Point p(3.0, 4.0);
constexpr double dist = p.distance(); // 编译期计算
// constexpr 构造函数和成员函数
class Complex {
private:
double real_, imag_;
public:
constexpr Complex(double real, double imag) : real_(real), imag_(imag) {}
constexpr Complex operator+(const Complex& other) const {
return Complex(real_ + other.real_, imag_ + other.imag_);
}
constexpr double real() const { return real_; }
constexpr double imag() const { return imag_; }
};
constexpr Complex c1(1.0, 2.0);
constexpr Complex c2(3.0, 4.0);
constexpr Complex c3 = c1 + c2; // 编译期计算
81. 范围 for 循环的使用?
简化遍历。语法 for (declaration : range) statement。适用于数组、容器、任何提供 begin/end 的类型。注意拷贝 vs 引用、const 修饰。
#include <vector>
#include <map>
#include <iostream>
#include <string>
void rangeForExample() {
// 数组遍历
int arr[] = {1, 2, 3, 4, 5};
for (int x : arr) {
std::cout << x << " ";
}
std::cout << std::endl;
// vector 遍历
std::vector<std::string> words = {"hello", "world", "c++"};
// 只读遍历
for (const auto& word : words) {
std::cout << word << " ";
}
std::cout << std::endl;
// 修改遍历
for (auto& word : words) {
word += "!";
}
// map 遍历
std::map<int, std::string> number_map = {{1, "one"}, {2, "two"}, {3, "three"}};
for (const auto& [key, value] : number_map) { // C++17 结构化绑定
std::cout << key << ": " << value << std::endl;
}
// 自定义类型 - 需要提供 begin/end
class MyContainer {
private:
std::vector<int> data_;
public:
MyContainer(std::initializer_list<int> init) : data_(init) {}
auto begin() { return data_.begin(); }
auto end() { return data_.end(); }
auto begin() const { return data_.begin(); }
auto end() const { return data_.end(); }
};
MyContainer container = {10, 20, 30};
for (int x : container) {
std::cout << x << " ";
}
std::cout << std::endl;
}
82. 结构化绑定的使用?
C++17 解包复合类型。语法 auto [a, b] = pair。适用于 pair、tuple、数组、结构体。简化多返回值处理。
#include <tuple>
#include <map>
#include <array>
#include <string>
#include <iostream>
void structuredBindingExample() {
// pair 解包
std::pair<int, std::string> p = {42, "answer"};
auto [id, name] = p;
std::cout << "id: " << id << ", name: " << name << std::endl;
// tuple 解包
std::tuple<int, double, std::string> t = {1, 3.14, "pi"};
auto [i, d, s] = t;
std::cout << i << ", " << d << ", " << s << std::endl;
// 数组解包
int arr[3] = {10, 20, 30};
auto [a, b, c] = arr;
std::cout << a << ", " << b << ", " << c << std::endl;
// 结构体解包
struct Point { double x, y; };
Point pt = {3.0, 4.0};
auto [x, y] = pt;
std::cout << "Point: (" << x << ", " << y << ")" << std::endl;
// map 遍历
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}, {"Charlie", 92}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << std::endl;
}
// 函数多返回值
auto divide = [](double numerator, double denominator) -> std::pair<double, bool> {
if (denominator == 0) {
return {0.0, false};
}
return {numerator / denominator, true};
};
auto [result, success] = divide(10.0, 2.0);
if (success) {
std::cout << "Result: " << result << std::endl;
}
}
83. std::optional 的使用?
表示可能不存在的值。替代空指针,类型安全。避免特殊值如 -1 表示无效。操作方法包括 value()、value_or()、has_value()。
#include <optional>
#include <string>
#include <iostream>
#include <vector>
// 函数可能返回值也可能不返回
std::optional<int> parseInt(const std::string& s) {
try {
return std::stoi(s);
} catch (...) {
return std::nullopt; // 表示没有值
}
}
// 查找元素
std::optional<std::string> findUser(int id) {
static std::map<int, std::string> users = {{1, "Alice"}, {2, "Bob"}, {3, "Charlie"}};
auto it = users.find(id);
if (it != users.end()) {
return it->second;
}
return std::nullopt;
}
void optionalExample() {
// 解析整数
auto result1 = parseInt("123");
auto result2 = parseInt("abc");
if (result1) {
std::cout << "Parsed: " << result1.value() << std::endl;
}
if (result2.has_value()) {
std::cout << "Parsed: " << result2.value() << std::endl;
} else {
std::cout << "Invalid number" << std::endl;
}
// 使用 value_or 提供默认值
int value = result2.value_or(0);
std::cout << "Value: " << value << std::endl;
// 查找用户
auto user = findUser(2);
std::cout << "User: " << user.value_or("Unknown") << std::endl;
// optional 作为成员变量
class Config {
private:
std::optional<std::string> database_url_;
std::optional<int> timeout_;
public:
void setDatabaseUrl(const std::string& url) {
database_url_ = url;
}
std::string getDatabaseUrl() const {
return database_url_.value_or("localhost:5432");
}
void setTimeout(int seconds) {
timeout_ = seconds;
}
int getTimeout() const {
return timeout_.value_or(30); // 默认 30 秒
}
};
Config config;
config.setTimeout(60);
std::cout << "Database: " << config.getDatabaseUrl() << std::endl;
std::cout << "Timeout: " << config.getTimeout() << std::endl;
}
84. std::variant 的使用?
类型安全的联合体。编译期类型检查。访问方式包括 std::get、std::visit。应用场景包括多类型选择、解析结果。
#include <variant>
#include <string>
#include <iostream>
#include <vector>
// variant 可以存储多种类型中的一种
using Value = std::variant<int, double, std::string>;
void variantExample() {
// 基本使用
Value v1 = 42;
Value v2 = 3.14;
Value v3 = "hello";
// 访问值
if (std::holds_alternative<int>(v1)) {
int int_val = std::get<int>(v1);
std::cout << "Int value: " << int_val << std::endl;
}
// 使用 get_if 安全访问
if (auto* str_ptr = std::get_if<std::string>(&v3)) {
std::cout << "String value: " << *str_ptr << std::endl;
}
// 使用 visitor 处理不同类型
auto visitor = [](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>) {
std::cout << "Int: " << arg << std::endl;
} else if constexpr (std::is_same_v<T, double>) {
std::cout << "Double: " << arg << std::endl;
} else if constexpr (std::is_same_v<T, std::string>) {
std::cout << "String: " << arg << std::endl;
}
};
std::visit(visitor, v1);
std::visit(visitor, v2);
std::visit(visitor, v3);
// variant 作为函数返回值
std::variant<int, std::string> parseNumber(const std::string& s) {
try {
return std::stoi(s);
} catch (...) {
return "Invalid number: " + s;
}
}
auto result = parseNumber("123");
std::visit([](auto&& arg) {
std::cout << "Parse result: " << arg << std::endl;
}, result);
// variant 容器
std::vector<Value> values = {1, 2.5, "three", 4, 5.5};
for (const auto& val : values) {
std::visit(visitor, val);
}
}
85. std::any 的使用?
类型安全的任意类型容器。类型擦除,存储任何类型。访问时需要类型检查,保存类型信息。
#include <any>
#include <string>
#include <iostream>
#include <vector>
#include <typeinfo>
void anyExample() {
// 基本使用
std::any a = 42;
a = 3.14;
a = std::string("hello");
// 访问值
if (a.type() == typeid(std::string)) {
std::string str = std::any_cast<std::string>(a);
std::cout << "String: " << str << std::endl;
}
// 使用 any_cast 安全访问
try {
int int_val = std::any_cast<int>(a); // 会抛出异常
} catch (const std::bad_any_cast& e) {
std::cout << "Bad cast: " << e.what() << std::endl;
}
// 使用指针访问避免异常
if (auto* str_ptr = std::any_cast<std::string>(&a)) {
std::cout << "String pointer: " << *str_ptr << std::endl;
}
// any 容器
std::vector<std::any> values;
values.push_back(42);
values.push_back(3.14);
values.push_back(std::string("world"));
values.push_back(std::vector<int>{1, 2, 3});
for (const auto& val : values) {
if (val.type() == typeid(int)) {
std::cout << "Int: " << std::any_cast<int>(val) << std::endl;
} else if (val.type() == typeid(double)) {
std::cout << "Double: " << std::any_cast<double>(val) << std::endl;
} else if (val.type() == typeid(std::string)) {
std::cout << "String: " << std::any_cast<std::string>(val) << std::endl;
} else if (val.type() == typeid(std::vector<int>)) {
auto vec = std::any_cast<std::vector<int>>(val);
std::cout << "Vector: ";
for (int x : vec) std::cout << x << " ";
std::cout << std::endl;
}
}
// any 作为函数参数
void printAny(const std::any& value) {
if (value.type() == typeid(int)) {
std::cout << "Int: " << std::any_cast<int>(value) << std::endl;
} else if (value.type() == typeid(std::string)) {
std::cout << "String: " << std::any_cast<std::string>(value) << std::endl;
} else {
std::cout << "Unknown type: " << value.type().name() << std::endl;
}
}
printAny(123);
printAny(std::string("test"));
// any 的实用场景:配置系统
class Config {
private:
std::map<std::string, std::any> config_;
public:
template<typename T>
void set(const std::string& key, const T& value) {
config_[key] = value;
}
template<typename T>
T get(const std::string& key, const T& default_value = T{}) const {
auto it = config_.find(key);
if (it != config_.end() && it->second.type() == typeid(T)) {
return std::any_cast<T>(it->second);
}
return default_value;
}
};
Config config;
config.set("timeout", 30);
config.set("name", std::string("MyApp"));
config.set("version", 1.5);
int timeout = config.get<int>("timeout", 60);
std::string name = config.get<std::string>("name", "Unknown");
double version = config.get<double>("version", 1.0);
std::cout << "Config: " << timeout << ", " << name << ", " << version << std::endl;
}
第五部分:设计模式与架构
设计模式
86. 单例模式的实现?
确保类只有一个实例。饿汉式(启动时创建)、懒汉式(首次使用时创建)。C++11 保证 Meyers 单例线程安全。
// 饿汉式单例
class EagerSingleton {
private:
static EagerSingleton instance;
EagerSingleton() = default;
public:
static EagerSingleton& getInstance() {
return instance;
}
void doSomething() {
std::cout << "Eager singleton working" << std::endl;
}
};
EagerSingleton EagerSingleton::instance;
// 懒汉式单例(Meyers 单例)- C++11 线程安全
class LazySingleton {
private:
LazySingleton() = default;
~LazySingleton() = default;
LazySingleton(const LazySingleton&) = delete;
LazySingleton& operator=(const LazySingleton&) = delete;
public:
static LazySingleton& getInstance() {
static LazySingleton instance; // C++11 保证线程安全
return instance;
}
void doSomething() {
std::cout << "Lazy singleton working" << std::endl;
}
};
// 使用示例
void singletonExample() {
LazySingleton& s1 = LazySingleton::getInstance();
LazySingleton& s2 = LazySingleton::getInstance();
std::cout << "Same instance: " << (&s1 == &s2) << std::endl;
s1.doSomething();
}
87. 工厂模式的实现?
创建对象而不暴露创建逻辑。简单工厂(一个工厂类)、工厂方法(每个产品对应工厂)、抽象工厂(产品族)。
#include <memory>
#include <string>
#include <iostream>
// 产品接口
class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
};
// 具体产品
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing Circle" << std::endl;
}
};
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing Rectangle" << std::endl;
}
};
class Triangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing Triangle" << std::endl;
}
};
// 简单工厂
class SimpleShapeFactory {
public:
enum class ShapeType { CIRCLE, RECTANGLE, TRIANGLE };
static std::unique_ptr<Shape> createShape(ShapeType type) {
switch (type) {
case ShapeType::CIRCLE:
return std::make_unique<Circle>();
case ShapeType::RECTANGLE:
return std::make_unique<Rectangle>();
case ShapeType::TRIANGLE:
return std::make_unique<Triangle>();
default:
return nullptr;
}
}
};
// 工厂方法模式
class ShapeFactory {
public:
virtual ~ShapeFactory() = default;
virtual std::unique_ptr<Shape> createShape() const = 0;
};
class CircleFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() const override {
return std::make_unique<Circle>();
}
};
class RectangleFactory : public ShapeFactory {
public:
std::unique_ptr<Shape> createShape() const override {
return std::make_unique<Rectangle>();
}
};
// 使用示例
void factoryExample() {
// 简单工厂
auto circle = SimpleShapeFactory::createShape(SimpleShapeFactory::ShapeType::CIRCLE);
auto rectangle = SimpleShapeFactory::createShape(SimpleShapeFactory::ShapeType::RECTANGLE);
circle->draw();
rectangle->draw();
// 工厂方法
CircleFactory circleFactory;
RectangleFactory rectangleFactory;
auto circle2 = circleFactory.createShape();
auto rectangle2 = rectangleFactory.createShape();
circle2->draw();
rectangle2->draw();
}
88. 观察者模式的实现?
定义对象间一对多依赖。主题维护观察者列表,观察者接收通知。松耦合。
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
// 观察者接口
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
// 主题接口
class Subject {
public:
virtual ~Subject() = default;
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify(const std::string& message) = 0;
};
// 具体主题
class NewsAgency : public Subject {
private:
std::vector<Observer*> observers;
std::string latestNews;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
observers.erase(
std::remove(observers.begin(), observers.end(), observer),
observers.end());
}
void notify(const std::string& message) override {
latestNews = message;
for (Observer* observer : observers) {
observer->update(message);
}
}
void setNews(const std::string& news) {
notify(news);
}
};
// 具体观察者
class NewsSubscriber : public Observer {
private:
std::string name;
public:
NewsSubscriber(const std::string& subscriberName) : name(subscriberName) {}
void update(const std::string& message) override {
std::cout << name << " received news: " << message << std::endl;
}
};
// 使用示例
void observerExample() {
NewsAgency agency;
NewsSubscriber alice("Alice");
NewsSubscriber bob("Bob");
NewsSubscriber charlie("Charlie");
agency.attach(&alice);
agency.attach(&bob);
agency.attach(&charlie);
agency.setNews("Breaking: C++20 is released!");
agency.detach(&bob);
agency.setNews("Update: New C++ features available!");
}
89. 策略模式的实现?
定义算法族,可互换使用。策略接口定义算法,具体策略实现算法,上下文使用策略对象。
#include <memory>
#include <vector>
#include <algorithm>
#include <iostream>
// 策略接口
class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) const = 0;
};
// 具体策略
class BubbleSort : public SortStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Using Bubble Sort" << std::endl;
int n = data.size();
for (int i = 0; i < n - 1; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
if (data[j] > data[j + 1]) {
std::swap(data[j], data[j + 1]);
}
}
}
}
};
class QuickSort : public SortStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Using Quick Sort" << std::endl;
std::sort(data.begin(), data.end());
}
};
class InsertionSort : public SortStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Using Insertion Sort" << std::endl;
for (size_t i = 1; i < data.size(); ++i) {
int key = data[i];
int j = static_cast<int>(i) - 1;
while (j >= 0 && data[j] > key) {
data[j + 1] = data[j];
j--;
}
data[j + 1] = key;
}
}
};
// 上下文
class Sorter {
private:
std::unique_ptr<SortStrategy> strategy;
public:
void setStrategy(std::unique_ptr<SortStrategy> newStrategy) {
strategy = std::move(newStrategy);
}
void sort(std::vector<int>& data) const {
if (strategy) {
strategy->sort(data);
}
}
};
// 使用示例
void strategyExample() {
std::vector<int> data = {64, 34, 25, 12, 22, 11, 90};
Sorter sorter;
// 使用冒泡排序
sorter.setStrategy(std::make_unique<BubbleSort>());
auto data1 = data;
sorter.sort(data1);
// 使用快速排序
sorter.setStrategy(std::make_unique<QuickSort>());
auto data2 = data;
sorter.sort(data2);
// 使用插入排序
sorter.setStrategy(std::make_unique<InsertionSort>());
auto data3 = data;
sorter.sort(data3);
}
90. 装饰器模式的实现?
动态添加对象功能。组件接口定义基础,具体组件实现基础,装饰器添加额外功能。
#include <string>
#include <iostream>
#include <memory>
// 组件接口
class Coffee {
public:
virtual ~Coffee() = default;
virtual std::string getDescription() const = 0;
virtual double cost() const = 0;
};
// 具体组件
class SimpleCoffee : public Coffee {
public:
std::string getDescription() const override {
return "Simple Coffee";
}
double cost() const override {
return 2.0;
}
};
// 装饰器基类
class CoffeeDecorator : public Coffee {
private:
std::unique_ptr<Coffee> coffee;
public:
CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {}
std::string getDescription() const override {
return coffee->getDescription();
}
double cost() const override {
return coffee->cost();
}
protected:
Coffee* getCoffee() const {
return coffee.get();
}
};
// 具体装饰器
class MilkDecorator : public CoffeeDecorator {
public:
MilkDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
std::string getDescription() const override {
return getCoffee()->getDescription() + ", Milk";
}
double cost() const override {
return getCoffee()->cost() + 0.5;
}
};
class SugarDecorator : public CoffeeDecorator {
public:
SugarDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
std::string getDescription() const override {
return getCoffee()->getDescription() + ", Sugar";
}
double cost() const override {
return getCoffee()->cost() + 0.2;
}
};
class VanillaDecorator : public CoffeeDecorator {
public:
VanillaDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
std::string getDescription() const override {
return getCoffee()->getDescription() + ", Vanilla";
}
double cost() const override {
return getCoffee()->cost() + 0.7;
}
};
// 使用示例
void decoratorExample() {
// 简单咖啡
auto coffee = std::make_unique<SimpleCoffee>();
std::cout << coffee->getDescription() << " $" << coffee->cost() << std::endl;
// 加牛奶的咖啡
auto milkCoffee = std::make_unique<MilkDecorator>(std::make_unique<SimpleCoffee>());
std::cout << milkCoffee->getDescription() << " $" << milkCoffee->cost() << std::endl;
// 加牛奶和糖的咖啡
auto milkSugarCoffee = std::make_unique<SugarDecorator>(
std::make_unique<MilkDecorator>(std::make_unique<SimpleCoffee>()));
std::cout << milkSugarCoffee->getDescription() << " $" << milkSugarCoffee->cost() << std::endl;
// 复杂组合
auto complexCoffee = std::make_unique<VanillaDecorator>(
std::make_unique<SugarDecorator>(
std::make_unique<MilkDecorator>(std::make_unique<SimpleCoffee>())));
std::cout << complexCoffee->getDescription() << " $" << complexCoffee->cost() << std::endl;
}
91. 适配器模式的实现?
使不兼容接口协同工作。目标接口客户端期望,适配者现有接口,适配器转换接口。
#include <string>
#include <iostream>
#include <memory>
// 目标接口
class MediaPlayer {
public:
virtual ~MediaPlayer() = default;
virtual void play(const std::string& audioType, const std::string& fileName) = 0;
};
// 适配者接口
class AdvancedMediaPlayer {
public:
virtual ~AdvancedMediaPlayer() = default;
virtual void playVlc(const std::string& fileName) = 0;
virtual void playMp4(const std::string& fileName) = 0;
};
// 具体适配者
class VlcPlayer : public AdvancedMediaPlayer {
public:
void playVlc(const std::string& fileName) override {
std::cout << "Playing vlc file: " << fileName << std::endl;
}
void playMp4(const std::string& fileName) override {
// 什么都不做
}
};
class Mp4Player : public AdvancedMediaPlayer {
public:
void playVlc(const std::string& fileName) override {
// 什么都不做
}
void playMp4(const std::string& fileName) override {
std::cout << "Playing mp4 file: " << fileName << std::endl;
}
};
// 适配器
class MediaAdapter : public MediaPlayer {
private:
std::unique_ptr<AdvancedMediaPlayer> advancedMusicPlayer;
public:
MediaAdapter(const std::string& audioType) {
if (audioType == "vlc") {
advancedMusicPlayer = std::make_unique<VlcPlayer>();
} else if (audioType == "mp4") {
advancedMusicPlayer = std::make_unique<Mp4Player>();
}
}
void play(const std::string& audioType, const std::string& fileName) override {
if (audioType == "vlc") {
advancedMusicPlayer->playVlc(fileName);
} else if (audioType == "mp4") {
advancedMusicPlayer->playMp4(fileName);
}
}
};
// 具体目标类
class AudioPlayer : public MediaPlayer {
private:
std::unique_ptr<MediaAdapter> mediaAdapter;
public:
void play(const std::string& audioType, const std::string& fileName) override {
// 内置支持 mp3
if (audioType == "mp3") {
std::cout << "Playing mp3 file: " << fileName << std::endl;
}
// 使用适配器支持其他格式
else if (audioType == "vlc" || audioType == "mp4") {
mediaAdapter = std::make_unique<MediaAdapter>(audioType);
mediaAdapter->play(audioType, fileName);
} else {
std::cout << "Invalid media. " << audioType << " format not supported" << std::endl;
}
}
};
// 使用示例
void adapterExample() {
AudioPlayer player;
player.play("mp3", "song.mp3");
player.play("mp4", "video.mp4");
player.play("vlc", "movie.vlc");
player.play("avi", "video.avi");
}
92. 命令模式的实现?
将请求封装为对象。命令接口声明执行,具体命令实现,调用者调用执行,接收者执行操作。
#include <string>
#include <iostream>
#include <vector>
#include <memory>
// 命令接口
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
};
// 接收者
class Light {
public:
void turnOn() {
std::cout << "Light is ON" << std::endl;
isOn = true;
}
void turnOff() {
std::cout << "Light is OFF" << std::endl;
isOn = false;
}
bool getState() const { return isOn; }
private:
bool isOn = false;
};
class Stereo {
public:
void on() { std::cout << "Stereo is ON" << std::endl; }
void off() { std::cout << "Stereo is OFF" << std::endl; }
void setCd() { std::cout << "Stereo is set for CD input" << std::endl; }
void setVolume(int volume) { std::cout << "Stereo volume set to " << volume << std::endl; }
};
// 具体命令
class LightOnCommand : public Command {
private:
Light* light;
public:
LightOnCommand(Light* l) : light(l) {}
void execute() override { light->turnOn(); }
void undo() override { light->turnOff(); }
};
class LightOffCommand : public Command {
private:
Light* light;
public:
LightOffCommand(Light* l) : light(l) {}
void execute() override { light->turnOff(); }
void undo() override { light->turnOn(); }
};
class StereoOnWithCdCommand : public Command {
private:
Stereo* stereo;
public:
StereoOnWithCdCommand(Stereo* s) : stereo(s) {}
void execute() override {
stereo->on();
stereo->setCd();
stereo->setVolume(11);
}
void undo() override { stereo->off(); }
};
// 调用者
class RemoteControl {
private:
std::vector<std::unique_ptr<Command>> undoStack;
std::unique_ptr<Command> currentCommand;
public:
void setCommand(std::unique_ptr<Command> command) {
currentCommand = std::move(command);
}
void buttonWasPressed() {
if (currentCommand) {
currentCommand->execute();
undoStack.push_back(std::move(currentCommand));
}
}
void undoButtonWasPressed() {
if (!undoStack.empty()) {
auto lastCommand = std::move(undoStack.back());
undoStack.pop_back();
lastCommand->undo();
}
}
};
// 使用示例
void commandExample() {
RemoteControl remote;
Light livingRoomLight;
Stereo stereo;
// 设置命令
remote.setCommand(std::make_unique<LightOnCommand>(&livingRoomLight));
remote.buttonWasPressed();
remote.setCommand(std::make_unique<StereoOnWithCdCommand>(&stereo));
remote.buttonWasPressed();
// 撤销操作
remote.undoButtonWasPressed();
remote.undoButtonWasPressed();
}
93. 模板方法模式的实现?
定义算法骨架,子类实现具体步骤。抽象类定义模板方法和抽象步骤,具体类实现具体步骤。
#include <iostream>
#include <string>
// 抽象类
class DataProcessor {
public:
// 模板方法 - 定义算法骨架
void processData() {
loadData();
if (validateData()) {
transformData();
saveData();
} else {
std::cout << "Data validation failed" << std::endl;
}
cleanup();
}
protected:
// 抽象步骤 - 子类必须实现
virtual void loadData() = 0;
virtual void transformData() = 0;
virtual void saveData() = 0;
// 钩子方法 - 子类可以重写
virtual bool validateData() { return true; }
virtual void cleanup() { std::cout << "Default cleanup" << std::endl; }
};
// 具体实现类
class CSVProcessor : public DataProcessor {
protected:
void loadData() override { std::cout << "Loading CSV data" << std::endl; }
void transformData() override { std::cout << "Transforming CSV data" << std::endl; }
void saveData() override { std::cout << "Saving CSV data" << std::endl; }
bool validateData() override {
std::cout << "Validating CSV format" << std::endl;
return true;
}
void cleanup() override { std::cout << "CSV specific cleanup" << std::endl; }
};
class JSONProcessor : public DataProcessor {
protected:
void loadData() override { std::cout << "Loading JSON data" << std::endl; }
void transformData() override { std::cout << "Transforming JSON data" << std::endl; }
void saveData() override { std::cout << "Saving JSON data" << std::endl; }
bool validateData() override {
std::cout << "Validating JSON structure" << std::endl;
return false; // 模拟验证失败
}
};
class XMLProcessor : public DataProcessor {
protected:
void loadData() override { std::cout << "Loading XML data" << std::endl; }
void transformData() override { std::cout << "Transforming JSON data" << std::endl; }
void saveData() override { std::cout << "Saving XML data" << std::endl; }
// 使用默认的 validateData 和 cleanup
};
// 使用示例
void templateMethodExample() {
std::cout << "Processing CSV:" << std::endl;
CSVProcessor csvProcessor;
csvProcessor.processData();
std::cout << "\nProcessing JSON:" << std::endl;
JSONProcessor jsonProcessor;
jsonProcessor.processData();
std::cout << "\nProcessing XML:" << std::endl;
XMLProcessor xmlProcessor;
xmlProcessor.processData();
}
94. 状态模式的实现?
允许对象在内部状态改变时改变行为。状态接口定义行为,具体状态实现行为,上下文维护当前状态。
#include <iostream>
#include <string>
#include <memory>
// 前向声明
class Context;
// 状态接口
class State {
public:
virtual ~State() = default;
virtual void insertCoin(Context* context) = 0;
virtual void ejectCoin(Context* context) = 0;
virtual void turnCrank(Context* context) = 0;
virtual void dispense(Context* context) = 0;
virtual std::string getName() const = 0;
};
// 上下文
class Context {
private:
std::unique_ptr<State> currentState;
int count = 0;
public:
Context(std::unique_ptr<State> initialState) : currentState(std::move(initialState)) {}
void setState(std::unique_ptr<State> newState) {
currentState = std::move(newState);
}
State* getState() const { return currentState.get(); }
int getCount() const { return count; }
void setCount(int c) { count = c; }
void insertCoin() { currentState->insertCoin(this); }
void ejectCoin() { currentState->ejectCoin(this); }
void turnCrank() { currentState->turnCrank(this); }
void dispense() { currentState->dispense(this); }
void releaseBall() {
std::cout << "A gumball comes rolling out the slot" << std::endl;
if (count > 0) {
count--;
}
}
};
// 具体状态
class NoCoinState : public State {
public:
void insertCoin(Context* context) override;
void ejectCoin(Context* context) override;
void turnCrank(Context* context) override;
void dispense(Context* context) override;
std::string getName() const override { return "NoCoinState"; }
};
class HasCoinState : public State {
public:
void insertCoin(Context* context) override;
void ejectCoin(Context* context) override;
void turnCrank(Context* context) override;
void dispense(Context* context) override;
std::string getName() const override { return "HasCoinState"; }
};
class SoldState : public State {
public:
void insertCoin(Context* context) override;
void ejectCoin(Context* context) override;
void turnCrank(Context* context) override;
void dispense(Context* context) override;
std::string getName() const override { return "SoldState"; }
};
class SoldOutState : public State {
public:
void insertCoin(Context* context) override;
void ejectCoin(Context* context) override;
void turnCrank(Context* context) override;
void dispense(Context* context) override;
std::string getName() const override { return "SoldOutState"; }
};
// 实现具体状态方法
void NoCoinState::insertCoin(Context* context) {
std::cout << "You inserted a coin" << std::endl;
context->setState(std::make_unique<HasCoinState>());
}
void NoCoinState::ejectCoin(Context* context) {
std::cout << "You haven't inserted a coin" << std::endl;
}
void NoCoinState::turnCrank(Context* context) {
std::cout << "You turned, but there's no coin" << std::endl;
}
void NoCoinState::dispense(Context* context) {
std::cout << "You need to pay first" << std::endl;
}
void HasCoinState::insertCoin(Context* context) {
std::cout << "You can't insert another coin" << std::endl;
}
void HasCoinState::ejectCoin(Context* context) {
std::cout << "Coin returned" << std::endl;
context->setState(std::make_unique<NoCoinState>());
}
void HasCoinState::turnCrank(Context* context) {
std::cout << "You turned..." << std::endl;
context->setState(std::make_unique<SoldState>());
}
void HasCoinState::dispense(Context* context) {
std::cout << "No gumball dispensed" << std::endl;
}
void SoldState::insertCoin(Context* context) {
std::cout << "Please wait, we're already giving you a gumball" << std::endl;
}
void SoldState::ejectCoin(Context* context) {
std::cout << "Sorry, you already turned the crank" << std::endl;
}
void SoldState::turnCrank(Context* context) {
std::cout << "Turning twice doesn't get you another gumball" << std::endl;
}
void SoldState::dispense(Context* context) {
context->releaseBall();
if (context->getCount() > 0) {
context->setState(std::make_unique<NoCoinState>());
} else {
context->setState(std::make_unique<SoldOutState>());
}
}
void SoldOutState::insertCoin(Context* context) {
std::cout << "You can't insert a coin, the machine is sold out" << std::endl;
}
void SoldOutState::ejectCoin(Context* context) {
std::cout << "You can't eject, you haven't inserted a coin yet" << std::endl;
}
void SoldOutState::turnCrank(Context* context) {
std::cout << "You turned, but there are no gumballs" << std::endl;
}
void SoldOutState::dispense(Context* context) {
std::cout << "No gumball dispensed" << std::endl;
}
// 使用示例
void stateExample() {
Context gumballMachine(std::make_unique<NoCoinState>());
gumballMachine.setCount(5);
std::cout << "Current state: " << gumballMachine.getState()->getName() << std::endl;
gumballMachine.insertCoin();
std::cout << "Current state: " << gumballMachine.getState()->getName() << std::endl;
gumballMachine.turnCrank();
std::cout << "Current state: " << gumballMachine.getState()->getName() << std::endl;
gumballMachine.insertCoin();
gumballMachine.ejectCoin();
std::cout << "Current state: " << gumballMachine.getState()->getName() << std::endl;
}
95. 代理模式的实现?
为其他对象提供代理以控制访问。主题接口定义公共接口,真实主题实现业务逻辑,代理控制访问。
#include <iostream>
#include <string>
#include <memory>
// 主题接口
class Image {
public:
virtual ~Image() = default;
virtual void display() = 0;
};
// 真实主题
class RealImage : public Image {
private:
std::string fileName;
void loadFromDisk() {
std::cout << "Loading " << fileName << " from disk" << std::endl;
}
public:
RealImage(const std::string& file) : fileName(file) {
loadFromDisk();
}
void display() override {
std::cout << "Displaying " << fileName << std::endl;
}
};
// 代理
class ProxyImage : public Image {
private:
std::unique_ptr<RealImage> realImage;
std::string fileName;
public:
ProxyImage(const std::string& file) : fileName(file) {}
void display() override {
if (!realImage) {
realImage = std::make_unique<RealImage>(fileName);
}
realImage->display();
}
};
// 使用示例
void proxyExample() {
std::cout << "Creating proxy images..." << std::endl;
Image* image1 = new ProxyImage("image1.jpg");
Image* image2 = new ProxyImage("image2.jpg");
std::cout << "Images created, but not loaded yet" << std::endl;
std::cout << "\nFirst display:" << std::endl;
image1->display();
std::cout << "\nSecond display:" << std::endl;
image1->display();
std::cout << "\nDisplaying second image:" << std::endl;
image2->display();
delete image1;
delete image2;
}
// 保护代理示例
class InternetAccess {
public:
virtual ~InternetAccess() = default;
virtual void connectTo(const std::string& serverHost) = 0;
};
class RealInternetAccess : public InternetAccess {
public:
void connectTo(const std::string& serverHost) override {
std::cout << "Connecting to " << serverHost << std::endl;
}
};
class ProxyInternetAccess : public InternetAccess {
private:
std::unique_ptr<RealInternetAccess> realInternet;
std::string username;
bool isAccessAllowed() const {
return username == "admin" || username == "user";
}
public:
ProxyInternetAccess(const std::string& user) : username(user) {}
void connectTo(const std::string& serverHost) override {
if (!isAccessAllowed()) {
std::cout << "Access denied for user: " << username << std::endl;
return;
}
if (!realInternet) {
realInternet = std::make_unique<RealInternetAccess>();
}
realInternet->connectTo(serverHost);
}
};
void protectionProxyExample() {
InternetAccess* adminAccess = new ProxyInternetAccess("admin");
InternetAccess* guestAccess = new ProxyInternetAccess("guest");
adminAccess->connectTo("google.com");
guestAccess->connectTo("google.com");
delete adminAccess;
delete guestAccess;
}
架构设计
96. SOLID 原则的解释?
SOLID 是面向对象设计的五个基本原则:单一职责、开闭、里氏替换、接口隔离、依赖倒置。
S - 单一职责原则 (SRP) 一个类应该只有一个引起变化的原因,每个类只负责一个功能。
// 违反 SRP
class User {
public:
void saveToDatabase() { /* 数据库操作 */ }
void validateEmail() { /* 验证逻辑 */ }
void sendEmail() { /* 邮件发送 */ }
};
// 遵循 SRP
class UserRepository {
public:
void save(const User& user) { /* 数据库操作 */ }
};
class EmailValidator {
public:
bool isValid(const std::string& email) { /* 验证逻辑 */ }
};
class EmailService {
public:
void send(const std::string& to, const std::string& message) { /* 发送邮件 */ }
};
O - 开闭原则 (OCP) 对扩展开放,对修改关闭。通过继承和组合实现扩展。
// 违反 OCP
class AreaCalculator {
public:
double calculate(const std::string& shape, double width, double height) {
if (shape == "rectangle") return width * height;
if (shape == "circle") return 3.14 * width * width;
return 0;
}
};
// 遵循 OCP
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0;
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override { return width * height; }
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14 * radius * radius; }
};
L - 里氏替换原则 (LSP) 子类必须能够替换其基类,子类不应破坏基类的约定。
// 违反 LSP
class Bird {
public:
virtual void fly() {
std::cout << "Flying" << std::endl;
}
};
class Penguin : public Bird {
public:
void fly() override {
throw std::runtime_error("Penguins can't fly");
}
};
// 遵循 LSP
class Bird {
public:
virtual ~Bird() = default;
virtual void makeSound() = 0;
};
class FlyingBird : public Bird {
public:
void fly() {
std::cout << "Flying" << std::endl;
}
};
class Penguin : public Bird {
public:
void makeSound() override {
std::cout << "Squawk" << std::endl;
}
};
I - 接口隔离原则 (ISP) 客户端不应依赖它不需要的接口,将大接口拆分为小接口。
// 违反 ISP
class Worker {
public:
virtual void work() = 0;
virtual void eat() = 0;
virtual void sleep() = 0;
};
class Robot : public Worker {
public:
void work() override { /* 工作 */ }
void eat() override { /* 机器人不需要吃饭 */ }
void sleep() override { /* 机器人不需要睡觉 */ }
};
// 遵循 ISP
class Workable {
public:
virtual ~Workable() = default;
virtual void work() = 0;
};
class Eatable {
public:
virtual ~Eatable() = default;
virtual void eat() = 0;
};
class Human : public Workable, public Eatable {
public:
void work() override { /* 工作 */ }
void eat() override { /* 吃饭 */ }
};
class Robot : public Workable {
public:
void work() override { /* 工作 */ }
};
D - 依赖倒置原则 (DIP) 高层模块不应依赖低层模块,都应依赖抽象。抽象不应依赖细节,细节应依赖抽象。
// 违反 DIP
class LightBulb {
public:
void turnOn() {
std::cout << "LightBulb on" << std::endl;
}
};
class Switch {
private:
LightBulb bulb;
public:
Switch() : bulb() {}
void press() {
bulb.turnOn();
}
};
// 遵循 DIP
class Switchable {
public:
virtual ~Switchable() = default;
virtual void turnOn() = 0;
virtual void turnOff() = 0;
};
class LightBulb : public Switchable {
public:
void turnOn() override {
std::cout << "LightBulb on" << std::endl;
}
void turnOff() override {
std::cout << "LightBulb off" << std::endl;
}
};
class Fan : public Switchable {
public:
void turnOn() override {
std::cout << "Fan on" << std::endl;
}
void turnOff() override {
std::cout << "Fan off" << std::endl;
}
};
class Switch {
private:
Switchable& device;
public:
Switch(Switchable& d) : device(d) {}
void press() {
device.turnOn();
}
};
97. 依赖注入的实现?
将依赖关系外部注入。构造函数注入、Setter 注入、接口注入。
#include <memory>
#include <iostream>
#include <string>
// 服务接口
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& message) = 0;
};
class IDatabase {
public:
virtual ~IDatabase() = default;
virtual void save(const std::string& data) = 0;
};
// 具体实现
class ConsoleLogger : public ILogger {
public:
void log(const std::string& message) override {
std::cout << "LOG: " << message << std::endl;
}
};
class FileLogger : public ILogger {
public:
void log(const std::string& message) override {
std::cout << "FILE LOG: " << message << std::endl;
}
};
class SQLDatabase : public IDatabase {
public:
void save(const std::string& data) override {
std::cout << "Saving to SQL: " << data << std::endl;
}
};
class NoSQLDatabase : public IDatabase {
public:
void save(const std::string& data) override {
std::cout << "Saving to NoSQL: " << data << std::endl;
}
};
// 使用依赖的类
class UserService {
private:
std::shared_ptr<ILogger> logger;
std::shared_ptr<IDatabase> database;
public:
// 构造函数注入
UserService(std::shared_ptr<ILogger> log, std::shared_ptr<IDatabase> db)
: logger(log), database(db) {}
// Setter 注入
void setLogger(std::shared_ptr<ILogger> log) { logger = log; }
void setDatabase(std::shared_ptr<IDatabase> db) { database = db; }
void createUser(const std::string& userData) {
logger->log("Creating user: " + userData);
database->save(userData);
logger->log("User created successfully");
}
};
// 简单的 DI 容器
class DIContainer {
private:
std::map<std::string, std::shared_ptr<void>> services;
public:
template<typename Interface, typename Implementation>
void registerService() {
services[typeid(Interface).name()] = std::make_shared<Implementation>();
}
template<typename T>
std::shared_ptr<T> resolve() {
auto it = services.find(typeid(T).name());
if (it != services.end()) {
return std::static_pointer_cast<T>(it->second);
}
return nullptr;
}
};
// 使用示例
void dependencyInjectionExample() {
// 手动依赖注入
auto logger = std::make_shared<ConsoleLogger>();
auto database = std::make_shared<SQLDatabase>();
UserService userService(logger, database);
userService.createUser("John Doe");
// 使用 DI 容器
DIContainer container;
container.registerService<ILogger, FileLogger>();
container.registerService<IDatabase, NoSQLDatabase>();
auto logger2 = container.resolve<ILogger>();
auto database2 = container.resolve<IDatabase>();
if (logger2 && database2) {
UserService userService2(logger2, database2);
userService2.createUser("Jane Smith");
}
}
98. 代码组织和模块化?
良好的代码组织和模块化。分层架构(表示层、业务层、数据层),模块划分按功能职责,接口设计清晰的模块边界。
// 数据访问层
namespace DataAccess {
class IRepository {
public:
virtual ~IRepository() = default;
virtual void save(const std::string& data) = 0;
virtual std::string load(int id) = 0;
};
class UserRepository : public IRepository {
public:
void save(const std::string& data) override {
std::cout << "Saving user to database: " << data << std::endl;
}
std::string load(int id) override {
return "User data for ID: " + std::to_string(id);
}
};
}
// 业务逻辑层
namespace BusinessLogic {
class UserService {
private:
std::shared_ptr<DataAccess::IRepository> repository;
public:
UserService(std::shared_ptr<DataAccess::IRepository> repo) : repository(repo) {}
bool createUser(const std::string& userData) {
if (userData.empty()) return false;
repository->save(userData);
return true;
}
std::string getUser(int id) {
return repository->load(id);
}
};
class ValidationService {
public:
bool validateEmail(const std::string& email) {
return email.find('@') != std::string::npos;
}
bool validatePassword(const std::string& password) {
return password.length() >= 8;
}
};
}
// 表示层
namespace Presentation {
class UserController {
private:
std::shared_ptr<BusinessLogic::UserService> userService;
std::shared_ptr<BusinessLogic::ValidationService> validationService;
public:
UserController(
std::shared_ptr<BusinessLogic::UserService> userSvc,
std::shared_ptr<BusinessLogic::ValidationService> validationSvc)
: userService(userSvc), validationService(validationSvc) {}
void registerUser(const std::string& email, const std::string& password) {
if (!validationService->validateEmail(email)) {
std::cout << "Invalid email format" << std::endl;
return;
}
if (!validationService->validatePassword(password)) {
std::cout << "Password too short" << std::endl;
return;
}
std::string userData = "Email: " + email + ", Password: " + password;
if (userService->createUser(userData)) {
std::cout << "User registered successfully" << std::endl;
} else {
std::cout << "Failed to register user" << std::endl;
}
}
};
}
// 应用程序入口
void modularExample() {
// 初始化各层
auto repository = std::make_shared<DataAccess::UserRepository>();
auto userService = std::make_shared<BusinessLogic::UserService>(repository);
auto validationService = std::make_shared<BusinessLogic::ValidationService>();
auto userController = std::make_shared<Presentation::UserController>(
userService, validationService);
// 使用
userController->registerUser("[email protected]", "password123");
userController->registerUser("invalid-email", "short");
}
99. 测试驱动开发 (TDD)?
TDD 开发流程:编写测试(先写失败的测试)、运行测试(确认失败)、编写代码(最小化代码使测试通过)、重构(优化代码保持测试通过)。
// 测试框架简化版
#define ASSERT_TRUE(condition) \nif (!(condition)) { \n std::cout << "FAIL: " << #condition << " at line " << __LINE__ << std::endl; \n} else { \n std::cout << "PASS: " << #condition << std::endl; \n}
#define ASSERT_EQ(expected, actual) \nif ((expected) != (actual)) { \n std::cout << "FAIL: expected " << (expected) << " but got " << (actual) << " at line " << __LINE__ << std::endl; \n} else { \n std::cout << "PASS: " << (expected) << " == " << (actual) << std::endl; \n}
// TDD 示例:计算器类
class Calculator {
public:
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
double divide(int a, int b) {
if (b == 0) throw std::runtime_error("Division by zero");
return static_cast<double>(a) / b;
}
};
// 测试用例
void testCalculator() {
Calculator calc;
// 测试加法
ASSERT_EQ(5, calc.add(2, 3));
ASSERT_EQ(0, calc.add(-2, 2));
ASSERT_EQ(-5, calc.add(-2, -3));
// 测试减法
ASSERT_EQ(1, calc.subtract(3, 2));
ASSERT_EQ(-1, calc.subtract(2, 3));
// 测试乘法
ASSERT_EQ(6, calc.multiply(2, 3));
ASSERT_EQ(0, calc.multiply(2, 0));
// 测试除法
ASSERT_EQ(2.0, calc.divide(4, 2));
ASSERT_EQ(1.5, calc.divide(3, 2));
// 测试除零异常
try {
calc.divide(1, 0);
ASSERT_TRUE(false); // 不应该到达这里
} catch (const std::runtime_error&) {
ASSERT_TRUE(true); // 期望的异常
}
}
// TDD 流程示例
class Stack {
private:
std::vector<int> data;
public:
void push(int item) { data.push_back(item); }
int pop() {
if (data.empty()) {
throw std::runtime_error("Stack is empty");
}
int item = data.back();
data.pop_back();
return item;
}
bool isEmpty() const { return data.empty(); }
int size() const { return data.size(); }
};
void testStack() {
Stack stack;
// 测试空栈
ASSERT_TRUE(stack.isEmpty());
ASSERT_EQ(0, stack.size());
// 测试 push
stack.push(1);
ASSERT_FALSE(stack.isEmpty());
ASSERT_EQ(1, stack.size());
stack.push(2);
ASSERT_EQ(2, stack.size());
// 测试 pop
ASSERT_EQ(2, stack.pop());
ASSERT_EQ(1, stack.size());
ASSERT_EQ(1, stack.pop());
ASSERT_TRUE(stack.isEmpty());
// 测试空栈 pop 异常
try {
stack.pop();
ASSERT_TRUE(false);
} catch (const std::runtime_error&) {
ASSERT_TRUE(true);
}
}
// 运行测试
void runTests() {
std::cout << "=== Calculator Tests ===" << std::endl;
testCalculator();
std::cout << "\n=== Stack Tests ===" << std::endl;
testStack();
}
100. C++ 最佳实践总结?
C++ 开发最佳实践:
代码风格
- 使用一致的命名约定
- 保持函数和类的职责单一
- 添加适当的注释和文档
- 遵循项目编码规范
内存管理
- 优先使用智能指针
- 遵循 RAII 原则
- 避免内存泄漏和野指针
- 合理使用移动语义
性能优化
- 选择合适的容器和算法
- 避免不必要的拷贝
- 使用编译期优化
- 进行性能分析
错误处理
- 使用异常处理错误情况
- 提供清晰的错误信息
- 保证异常安全
- 合理使用 noexcept
现代 C++ 特性
- 使用 auto 简化代码
- 利用范围 for 循环
- 使用 lambda 表达式
- 采用智能指针管理资源
设计原则
- 遵循 SOLID 原则
- 使用设计模式解决常见问题
- 保持代码的可测试性
- 考虑代码的可维护性
// 最佳实践示例
class ModernCppExample {
private:
std::unique_ptr<int[]> data_;
size_t size_;
public:
// 使用智能指针和移动语义
ModernCppExample(size_t size) : size_(size) {
data_ = std::make_unique<int[]>(size_);
std::fill_n(data_.get(), size_, 0);
}
// 使用默认的拷贝/移动操作
ModernCppExample(const ModernCppExample&) = delete;
ModernCppExample& operator=(const ModernCppExample&) = delete;
ModernCppExample(ModernCppExample&&) = default;
ModernCppExample& operator=(ModernCppExample&&) = default;
// 使用 const 和 noexcept
size_t size() const noexcept { return size_; }
// 使用范围 for 循环友好的接口
int* begin() { return data_.get(); }
int* end() { return data_.get() + size_; }
const int* begin() const { return data_.get(); }
const int* end() const { return data_.get() + size_; }
// 使用异常处理
void at(size_t index, int value) {
if (index >= size_) {
throw std::out_of_range("Index out of range");
}
data_[index] = value;
}
// 使用 constexpr 和 auto
constexpr static size_t max_size() noexcept { return 1000000; }
};
// 使用示例
void bestPracticeExample() {
try {
ModernCppExample example(10);
// 使用 auto 和范围 for 循环
for (auto& item : example) {
item = 42;
}
// 使用 STL 算法
std::sort(example.begin(), example.end());
std::cout << "Size: " << example.size() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
总结
这 100 道 C++ 面试题涵盖了现代 C++ 开发的核心知识点:
核心技能
- 基础语法:面向对象、继承多态、运算符重载
- 现代特性:智能指针、lambda、移动语义、并发编程
- STL 精通:容器、算法、迭代器、函数对象
- 设计模式:常用模式的理解和应用
进阶能力
- 内存管理:RAII、智能指针、异常安全
- 性能优化:移动语义、编译期优化、缓存友好
- 并发编程:线程、互斥锁、原子操作、异步编程
- 架构设计:SOLID 原则、依赖注入、模块化
面试准备建议
- 理解原理:不要死记硬背,理解底层机制
- 动手实践:多写代码,积累实际经验
- 关注现代 C++:掌握 C++11/14/17/20 新特性
- 项目经验:结合实际项目理解概念
- 持续学习:C++ 标准不断更新,保持学习

