C++经典面试题100道
📋 目录提纲
🎯 第一部分:C++基础语法 (1-25题)
- 面向对象基础 (1-8):类、对象、构造函数、析构函数
- 访问控制与封装 (9-13):public/private/protected、友元
- 继承与多态 (14-20):继承方式、虚函数、纯虚函数、抽象类
- 运算符重载 (21-25):重载规则、常用运算符重载
🔧 第二部分:C++核心特性 (26-50题)
- 模板编程 (26-32):函数模板、类模板、模板特化
- STL容器 (33-40):vector、list、map、set等容器特性
- STL算法 (41-45):sort、find、transform等常用算法
- 智能指针 (46-50):unique_ptr、shared_ptr、weak_ptr
⚡ 第三部分:内存管理与性能 (51-70题)
- 内存管理 (51-58):new/delete、内存布局、对象生命周期
- 异常处理 (59-63):try-catch、异常安全、RAII
- 性能优化 (64-70):移动语义、完美转发、编译优化
🏗️ 第四部分:高级特性 (71-85题)
- 多线程编程 (71-77):thread、mutex、condition_variable、atomic
- 现代C++特性 (78-85):auto、lambda、constexpr、智能指针
🎨 第五部分:设计模式与架构 (86-100题)
- 设计模式 (86-95):单例、工厂、观察者等常用模式
- 架构设计 (96-100):SOLID原则、代码组织、最佳实践
🎯 第一部分:C++基础语法 (1-25题)
📚 面向对象基础 (1-8)
1. 什么是面向对象编程?C++中如何体现?
答案:
面向对象编程(OOP)是一种编程范式,通过对象来组织代码。C++中的体现:
- 封装:将数据和操作数据的方法封装在类中
- 继承:子类继承父类的属性和方法
- 多态:同一接口的不同实现
class Animal {
public:
virtual void makeSound() = 0; // 纯虚函数
};
class Dog : public Animal {
public:
void makeSound() override { cout << "Woof!" << 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 string& name, int age)
: name_(name), age_(age) { // 初始化列表
// 构造函数体
}
private:
string name_;
int age_;
};
8. this指针的作用?
答案:
this指针指向当前对象,用于:
- 区分成员变量和参数
- 返回当前对象的引用
- 在成员函数中访问对象
class Student {
public:
Student& setName(const string& name) {
this->name = name; // 区分成员变量
return *this; // 返回当前对象引用
}
private:
string name;
};
🔐 访问控制与封装 (9-13)
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) {
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成员函数承诺不修改对象状态:
- 可以被const对象调用
- 不能修改非mutable成员变量
- 提高代码安全性和可读性
class Rectangle {
public:
double getArea() const { // const成员函数
return width * height; // 只读操作
}
void setWidth(double w) { width = w; } // 非const函数
private:
double width, height;
};
13. mutable关键字的作用?
答案:
mutable允许在const成员函数中修改成员变量:
- 用于需要在逻辑上保持const但需要修改的成员
- 常用于缓存、计数器等
class Calculator {
public:
int getResult() const {
cache_count++; // mutable成员可在const函数中修改
return compute();
}
private:
mutable int cache_count = 0; // mutable成员
int compute() const { return 42; }
};
🧬 继承与多态 (14-20)
14. 继承的方式有哪些?区别是什么?
答案:
C++有三种继承方式:
- public继承:基类public→派生类public,protected→protected
- protected继承:基类public→派生类protected,protected→protected
- private继承:基类public→派生类private,protected→private
class Base {
public: int pub;
protected: int prot;
private: int priv;
};
class PublicDerived : public Base {
// pub保持public,prot保持protected,priv不可访问
};
class PrivateDerived : private Base {
// pub和prot都变成private,priv不可访问
};
15. 什么是虚函数?作用是什么?
答案:
虚函数实现运行时多态:
- 允许通过基类指针调用派生类的实现
- 需要virtual关键字声明
- 通过虚函数表实现
class Shape {
public:
virtual double getArea() const = 0; // 纯虚函数
virtual void draw() const { cout << "Drawing shape" << endl; }
};
class Circle : public Shape {
public:
double getArea() const override { return 3.14 * radius * radius; }
void draw() const override { cout << "Drawing circle" << endl; }
private:
double radius;
};
16. 纯虚函数和抽象类的概念?
答案:
- 纯虚函数:没有实现的虚函数,
= 0表示 - 抽象类:包含纯虚函数的类,不能实例化
class Animal { // 抽象类
public:
virtual void makeSound() = 0; // 纯虚函数
virtual void eat() { cout << "Eating" << endl; } // 普通虚函数
};
// Animal animal; // Error:不能实例化抽象类
class Dog : public Animal {
public:
void makeSound() override { cout << "Woof!" << endl; } // 必须实现
};
17. 什么是虚函数表(vtable)?
答案:
虚函数表是实现多态的机制:
- 每个有虚函数的类都有一个虚函数表
- 对象中包含指向虚函数表的指针
- 虚函数表存储虚函数的地址
// 编译器为这个类生成虚函数表
class Base {
public:
virtual void func1() {}
virtual void func2() {}
};
// 内存布局:[vptr][成员变量]
// vptr指向虚函数表:[&Base::func1, &Base::func2]
18. override关键字的作用?
答案:
override(C++11)明确表示重写基类虚函数:
- 编译器检查是否真的重写了基类函数
- 防止拼写错误或参数不匹配
- 提高代码可读性
class Base {
public:
virtual void process(int value) {}
};
class Derived : public Base {
public:
void process(int value) override { // 明确表示重写
// 如果基类没有对应的虚函数,编译器会报错
}
};
19. final关键字的作用?
答案:
final(C++11)用于阻止继承或重写:
- 修饰类:不能被继承
- 修饰虚函数:不能被重写
class FinalClass final { // 不能被继承
public:
virtual void method() final { // 不能被重写
// 实现
}
};
// class Derived : public FinalClass {} // Error:不能继承final类
20. 多重继承的问题和解决方案?
答案:
多重继承的问题:
- 菱形继承:二义性
- 数据冗余:基类成员重复
解决方案:
- 虚继承:解决菱形继承问题
- 明确作用域:使用类名限定
class Animal {
public:
void eat() { cout << "Eating" << endl; }
};
class Mammal : virtual public Animal {}; // 虚继承
class Bird : virtual public Animal {}; // 虚继承
class Bat : public Mammal, public Bird {
public:
void test() {
eat(); // OK:没有二义性
}
};
⚙️ 运算符重载 (21-25)
21. 运算符重载的规则和限制?
答案:
规则和限制:
- 不能重载的运算符:
.::sizeoftypeid.*?: - 必须重载为成员函数:
=()[]-> - 不能改变运算符的优先级和结合性
- 不能创建新的运算符
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 out_of_range("Index out of range");
return data[index];
}
const int& operator[](size_t index) const {
if (index >= size) throw 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;
cout << add(3, 4) << endl; // 输出7
Multiplier times3(3);
cout << times3(5) << endl; // 输出15
🔧 第二部分:C++核心特性 (26-50题)
📝 模板编程 (26-32)
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:
vector<T> elements;
public:
void push(const T& elem) {
elements.push_back(elem);
}
T pop() {
if (elements.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
T elem = elements.back();
elements.pop_back();
return elem;
}
};
// 使用
Stack<int> intStack;
Stack<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) cout << "Setting index " << index << endl;
data[index] = value;
}
};
// 模板模板参数
template<typename T, template<typename, typename> class Container>
class Adapter {
private:
Container<T, allocator<T>> container;
public:
void add(const T& item) { container.push_back(item); }
};
30. SFINAE是什么?
答案:
SFINAE(Substitution Failure Is Not An Error):
- 替换失败不是错误
- 模板参数替换失败时,不会编译错误,而是寻找其他重载
- 常用于模板元编程和类型萃取
template<typename T>
typename T::value_type test(typename T::iterator*); // 如果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); // 显式实例化int版本
32. 模板的优缺点?
答案:
优点:
- 代码复用:一套代码支持多种类型
- 类型安全:编译期类型检查
- 性能:与手写代码性能相同
缺点:
- 编译时间长:每个使用都实例化
- 代码膨胀:生成多个版本的代码
- 调试困难:错误信息复杂
- 编译器支持:老旧编译器支持有限
📦 STL容器 (33-40)
33. vector的特点和适用场景?
答案:
vector是动态数组:
- 特点:连续内存、随机访问、自动扩容
- 时间复杂度:访问O(1)、插入删除O(n)
- 适用场景:频繁随机访问、尾部插入
vector<int> vec;
vec.push_back(1); // 尾部插入
vec[0] = 10; // 随机访问
vec.size(); // 元素个数
vec.capacity(); // 容量
vec.reserve(100); // 预分配容量
34. list的特点和适用场景?
答案:
list是双向链表:
- 特点:非连续内存、双向遍历、任意位置插入删除
- 时间复杂度:访问O(n)、插入删除O(1)
- 适用场景:频繁插入删除、不需要随机访问
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 - 有序
map<string, int> ordered_map;
ordered_map["apple"] = 1;
ordered_map["banana"] = 2;
// unordered_map - 无序但更快
unordered_map<string, int> unordered_map;
unordered_map["apple"] = 1;
unordered_map["banana"] = 2;
36. set和multiset的区别?
答案:
- set:元素唯一、自动排序
- multiset:元素可重复、自动排序
set<int> s;
s.insert(1);
s.insert(1); // 重复插入失败
// s = {1}
multiset<int> ms;
ms.insert(1);
ms.insert(1); // 重复插入成功
// ms = {1, 1}
37. deque的特点?
答案:
deque是双端队列:
- 特点:两端插入删除高效、非严格连续内存
- 时间复杂度:两端操作O(1)、中间操作O(n)
- 适用场景:需要在两端频繁操作
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:在指定位置构造
- emplace_back:在尾部构造
- emplace_front:在头部构造
vector<pair<int, string>> vec;
// 传统方式:构造临时对象
vec.push_back(make_pair(1, "hello"));
// emplace方式:直接构造
vec.emplace_back(1, "hello"); // 更高效
40. 容器的空间配置器?
答案:
空间配置器管理内存分配:
- 默认allocator:使用new/delete
- 自定义allocator:特殊内存管理需求
- 内存池:提高分配效率
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);
}
};
// 使用自定义分配器
vector<int, MyAllocator<int>> vec;
🔧 STL算法 (41-45)
41. sort算法的使用和注意事项?
答案:
sort是快速排序实现:
- 时间复杂度:平均O(n log n),最坏O(n²)
- 稳定性:不稳定排序
- 自定义比较:可提供比较函数
vector<int> vec = {3, 1, 4, 1, 5};
// 默认升序
sort(vec.begin(), vec.end());
// 降序
sort(vec.begin(), vec.end(), greater<int>());
// 自定义比较
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),需要有序序列
vector<int> vec = {1, 3, 5, 7, 9};
// find - 线性搜索
auto it1 = find(vec.begin(), vec.end(), 5);
// binary_search - 二分搜索(需要先排序)
bool found = binary_search(vec.begin(), vec.end(), 5);
// lower_bound/upper_bound - 找范围
auto lower = lower_bound(vec.begin(), vec.end(), 5);
auto upper = upper_bound(vec.begin(), vec.end(), 5);
43. for_each和范围for循环的区别?
答案:
- for_each:算法函数,可配合函数对象
- 范围for:语言特性,更简洁
vector<int> vec = {1, 2, 3, 4, 5};
// for_each
for_each(vec.begin(), vec.end(), [](int& x) { x *= 2; });
// 范围for循环
for (int& x : vec) {
x *= 2;
}
44. transform算法的使用?
答案:
transform对序列进行变换:
- 一元变换:对每个元素应用函数
- 二元变换:对两个序列对应元素应用函数
vector<int> src = {1, 2, 3, 4, 5};
vector<int> dest(src.size());
// 一元变换
transform(src.begin(), src.end(), dest.begin(),
[](int x) { return x * x; });
// 二元变换
vector<int> src2 = {5, 4, 3, 2, 1};
vector<int> dest2(src.size());
transform(src.begin(), src.end(), src2.begin(), dest2.begin(),
[](int a, int b) { return a + b; });
45. remove和erase的区别?
答案:
- remove:将不需要的元素移到末尾,返回新逻辑终点
- erase:真正删除元素
vector<int> vec = {1, 2, 3, 4, 5, 3, 6};
// remove-erase惯用法
auto new_end = remove(vec.begin(), vec.end(), 3); // 移动3到末尾
vec.erase(new_end, vec.end()); // 真正删除
// 一行完成
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end());
🧠 智能指针 (46-50)
46. unique_ptr的特点和使用?
答案:
unique_ptr是独占所有权的智能指针:
- 独占所有权:不能复制,只能移动
- 自动释放:析构时自动delete
- 轻量级:开销接近原始指针
unique_ptr<int> ptr1 = make_unique<int>(42);
// unique_ptr<int> ptr2 = ptr1; // Error:不能复制
unique_ptr<int> ptr2 = move(ptr1); // OK:移动
// 自定义删除器
unique_ptr<FILE, decltype(&fclose)> file(fopen("test.txt", "r"), fclose);
47. shared_ptr的特点和使用?
答案:
shared_ptr是共享所有权的智能指针:
- 引用计数:多个指针共享对象
- 线程安全:引用计数操作是原子的
- 循环引用:可能导致内存泄漏
shared_ptr<int> ptr1 = make_shared<int>(42);
shared_ptr<int> ptr2 = ptr1; // 引用计数+1
cout << ptr1.use_count() << endl; // 输出2
ptr2.reset(); // 引用计数-1
cout << ptr1.use_count() << endl; // 输出1
48. weak_ptr的作用?
答案:
weak_ptr解决shared_ptr的循环引用问题:
- 弱引用:不影响引用计数
- 观察者:观察shared_ptr管理的对象
- lock():尝试获取shared_ptr
class Node {
public:
shared_ptr<Node> next;
weak_ptr<Node> prev; // 使用weak_ptr避免循环引用
};
shared_ptr<Node> node1 = make_shared<Node>();
shared_ptr<Node> node2 = make_shared<Node>();
node1->next = node2;
node2->prev = node1; // weak_ptr不增加引用计数
49. make_shared和make_unique的优势?
答案:
优势:
- 异常安全:避免内存泄漏
- 性能更好:一次内存分配
- 代码简洁:更易读
// 传统方式
shared_ptr<int> ptr(new int(42)); // 两次分配:对象+控制块
// make_shared方式
auto ptr = make_shared<int>(42); // 一次分配:对象+控制块一起
50. 智能指针的选择策略?
答案:
选择策略:
- unique_ptr:独占所有权,最轻量
- shared_ptr:需要共享所有权
- weak_ptr:解决循环引用,观察者模式
- 原始指针:不拥有所有权,仅观察
// 选择指南
class Owner {
unique_ptr<Resource> resource; // 独占资源
};
class Observer {
weak_ptr<Subject> subject; // 观察主题
};
class Collaborator {
shared_ptr<Data> data; // 协作数据
};
⚡ 第三部分:内存管理与性能 (51-70题)
💾 内存管理 (51-58)
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是什么?
答案:
RAII(Resource Acquisition Is Initialization):
- 资源获取即初始化
- 构造函数获取资源,析构函数释放资源
- 异常安全:即使异常也能正确释放
- 智能指针:RAII的典型应用
class FileHandler {
public:
FileHandler(const char* filename) {
file = fopen(filename, "r");
if (!file) throw 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() { cout << "构造" << endl; }
~Lifecycle() { cout << "析构" << endl; }
};
// 不同存储期的对象
Lifecycle global_obj; // 静态存储期,程序结束时销毁
void func() {
static Lifecycle static_obj; // 静态存储期,程序结束时销毁
Lifecycle local_obj; // 自动存储期,函数结束时销毁
auto* dynamic_obj = new Lifecycle(); // 动态存储期,delete时销毁
delete dynamic_obj;
}
57. 内存泄漏的检测和预防?
答案:
检测方法:
- Valgrind:Linux下内存检测工具
- AddressSanitizer:编译器内置检测
- 智能指针:自动管理内存
- 代码审查:人工检查
预防策略:
- RAII:资源自动管理
- 智能指针:避免手动delete
- 代码规范:明确的内存管理规则
// 使用智能指针预防泄漏
class ResourceManager {
public:
void addResource(shared_ptr<Resource> resource) {
resources.push_back(resource);
}
private:
vector<shared_ptr<Resource>> resources;
};
58. 自定义内存管理?
答案:
自定义内存管理场景:
- 性能优化:减少分配开销
- 特殊需求:固定大小块、内存池
- 调试目的:跟踪内存使用
class MemoryPool {
private:
struct Block {
Block* next;
};
Block* freeList = nullptr;
size_t blockSize;
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-63)
59. C++异常处理机制?
答案:
异常处理三要素:
- try:可能抛出异常的代码块
- catch:捕获并处理异常
- throw:抛出异常
try {
// 可能抛出异常的代码
if (error) {
throw runtime_error("Something went wrong");
}
} catch (const runtime_error& e) {
cout << "捕获异常: " << e.what() << endl;
} catch (...) {
cout << "捕获未知异常" << endl;
}
60. 异常安全的级别?
答案:
异常安全三个级别:
- 基本保证:无资源泄漏,对象状态有效
- 强保证:操作成功或回滚到原状态
- 不抛异常保证:noexcept,绝不抛出异常
// 基本保证
void basicGuarantee() {
auto* ptr = new int[100];
try {
// 可能抛出异常的操作
process(ptr);
} catch (...) {
delete[] ptr; // 确保资源释放
throw; // 重新抛出
}
delete[] ptr;
}
// 强保证
void strongGuarantee(vector<int>& vec) {
vector<int> backup = vec; // 备份
try {
// 修改操作
modify(vec);
} catch (...) {
vec = backup; // 回滚
throw;
}
}
// 不抛异常保证
void noThrowGuarantee() noexcept {
// 绝不抛出异常的代码
}
61. noexcept的作用?
答案:
noexcept指定函数不抛出异常:
- 性能优化:编译器可以生成更优代码
- 程序终止:抛出异常时直接terminate
- 移动语义:移动操作通常标记noexcept
// noexcept函数
void safeFunction() noexcept {
// 如果抛出异常,程序会调用terminate()
}
// 条件noexcept
template<typename T>
void moveIfNoexcept(T& src, T& dest) noexcept(noexcept(T(move(src)))) {
dest = move(src);
}
// 移动构造函数通常标记noexcept
class MyClass {
public:
MyClass(MyClass&& other) noexcept : data(move(other.data)) {}
private:
vector<int> data;
};
62. 异常和错误码的比较?
答案:
| 特性 | 异常 | 错误码 |
|---|---|---|
| 性能 | 较高开销 | 低开销 |
| 可读性 | 清晰分离 | 混合逻辑 |
| 强制性 | 强制处理 | 可忽略 |
| 信息量 | 丰富 | 有限 |
| 适用场景 | 异常情况 | 预期错误 |
// 异常方式
int divide(int a, int b) {
if (b == 0) {
throw 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在异常处理中的作用?
答案:
RAII确保异常安全:
- 自动清理:析构函数自动调用
- 资源管理:智能指针、文件句柄等
- 异常传播:资源正确释放
class ExceptionSafe {
public:
ExceptionSafe() {
resource1 = acquireResource1();
resource2 = acquireResource2(); // 可能抛出异常
// 如果resource2获取失败,resource1会自动释放
}
~ExceptionSafe() {
releaseResource2(resource2);
releaseResource1(resource1);
}
private:
Resource* resource1;
Resource* resource2;
};
🚀 性能优化 (64-70)
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 = move(s1); // 移动构造
String s4 = String("World"); // 移动构造(临时对象)
65. 完美转发的概念?
答案:
完美转发保持参数的值类别:
- 转发引用:template T&&
- forward函数:保持左值/右值特性
- 通用引用:可以绑定左值和右值
template<typename T>
void wrapper(T&& arg) {
// 完美转发arg到func
func(forward<T>(arg));
}
void func(int& x) { cout << "左值引用" << endl; }
void func(int&& x) { cout << "右值引用" << endl; }
int a = 10;
wrapper(a); // 转发为左值引用
wrapper(10); // 转发为右值引用
66. 返回值优化(RVO)?
答案:
RVO(Return Value Optimization):
- 编译器优化:避免不必要的拷贝
- 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:释放多余容量
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:GNU性能分析器
- perf:Linux性能工具
- 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-85题)
🧵 多线程编程 (71-77)
71. C++11线程的创建和管理?
答案:
C++11线程支持:
- std::thread:线程类
- join():等待线程结束
- detach():分离线程
- 硬件并发:thread::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();
// 或者分离线程
// t1.detach();
// 获取硬件并发数
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()};
// CAS操作
while (!head.compare_exchange_weak(newNode->next, newNode)) {
// 重试
}
}
};
75. 线程安全的单例模式?
答案:
线程安全单例实现:
- Meyers单例:C++11保证线程安全
- 双重检查锁定:传统方法
- std::call_once:C++11保证
// 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的使用?
答案:
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-85)
78. auto关键字的使用?
答案:
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表达式的使用?
答案:
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
- 限制:只能使用编译期可用的操作
// 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循环简化遍历:
- 语法: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的使用?
答案:
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); // 如果没有值,使用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::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);
}
}
// 使用variant实现表达式求值
class Expression {
public:
using Value = std::variant<int, double>;
enum class Op { Add, Subtract, Multiply, Divide };
Expression(Value val) : value_(val), op_(std::nullopt) {}
Expression(Op op, Expression left, Expression right)
: op_(op), left_(std::make_unique<Expression>(left)),
right_(std::make_unique<Expression>(right)) {}
Value evaluate() const {
if (op_) {
auto left_val = left_->evaluate();
auto right_val = right_->evaluate();
return std::visit([this](auto&& l, auto&& r) -> Value {
using L = std::decay_t<decltype(l)>;
using R = std::decay_t<decltype(r)>;
if constexpr (std::is_same_v<L, int> && std::is_same_v<R, int>) {
switch (*op_) {
case Op::Add: return l + r;
case Op::Subtract: return l - r;
case Op::Multiply: return l * r;
case Op::Divide: return r != 0 ? l / r : 0;
}
} else {
// 转换为double进行计算
double dl = static_cast<double>(l);
double dr = static_cast<double>(r);
switch (*op_) {
case Op::Add: return dl + dr;
case Op::Subtract: return dl - dr;
case Op::Multiply: return dl * dr;
case Op::Divide: return dr != 0.0 ? dl / dr : 0.0;
}
}
return 0;
}, left_val, right_val);
}
return value_;
}
private:
Value value_;
std::optional<Op> op_;
std::unique_ptr<Expression> left_, right_;
};
85. std::any的使用?
答案:
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-100题)
🏛️ 设计模式 (86-95)
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. 观察者模式的实现?
答案:
观察者模式定义对象间一对多依赖:
- 主题(Subject):维护观察者列表
- 观察者(Observer):接收主题通知
- 松耦合:主题和观察者解耦
#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;
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();
}
}
private:
std::unique_ptr<Command> currentCommand;
};
// 使用示例
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 XML 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-100)
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. 依赖注入的实现?
答案:
依赖注入(DI)将依赖关系外部注入:
- 构造函数注入:通过构造函数注入依赖
- Setter注入:通过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("john@example.com", "password123");
userController->registerUser("invalid-email", "short");
}
99. 测试驱动开发(TDD)?
答案:
TDD开发流程:
- 编写测试:先写失败的测试
- 运行测试:确认测试失败
- 编写代码:最小化代码使测试通过
- 重构:优化代码保持测试通过
// 测试框架简化版
#define ASSERT_TRUE(condition) \
if (!(condition)) { \
std::cout << "FAIL: " << #condition << " at line " << __LINE__ << std::endl; \
} else { \
std::cout << "PASS: " << #condition << std::endl; \
}
#define ASSERT_EQ(expected, actual) \
if ((expected) != (actual)) { \
std::cout << "FAIL: expected " << (expected) << " but got " << (actual) << " at line " << __LINE__ << std::endl; \
} else { \
std::cout << "PASS: " << (expected) << " == " << (actual) << std::endl; \
}
// 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++标准不断更新,保持学习
祝你面试成功!🎉
3万+

被折叠的 条评论
为什么被折叠?



