内存管理最佳实践
定义与作用
现代 C++(C++11 及以后)的内存管理范式已经从"手动 new/delete"转变为"RAII + 智能指针优先":
// ❌ C++03 风格
int* p = new int(42);
// ... 忘记 delete → 泄漏
// ✅ 现代 C++ 风格
auto p = std::make_unique<int>(42);
// 离开作用域自动释放
核心原则
完整示例
示例一:从原始指针到智能指针的迁移
#include <iostream>
#include <memory>
#include <vector>
#include <string>
// ====== C++03 风格(危险) ======
struct StudentCpp03 {
std::string name;
int* scores; // 裸指针——谁负责释放?
int count;
StudentCpp03(const std::string& n, int c)
: name(n), scores(new int[c]), count(c) {}
// 拷贝构造?拷贝赋值?析构?Rule of Three 常被忘记
~StudentCpp03() { delete[] scores; }
};
// ====== 现代 C++ 风格(安全) ======
struct StudentModern {
std::string name;
std::vector<int> scores; // vector 自动管理内存
StudentModern(std::string n, std::vector<int> s)
: name(std::move(n)), scores(std::move(s)) {}
// 零规则(Rule of Zero):编译器生成的拷贝/移动/析构全部正确
};
// ====== 需要动态分配时 ======
class StudentDatabase {
public:
void add_student(std::string name, std::vector<int> scores) {
students_.push_back(
std::make_unique<StudentModern>(std::move(name), std::move(scores)));
}
void print_all() const {
for (const auto& s : students_) {
std::cout << " " << s->name << ": ";
for (int sc : s->scores) std::cout << sc << " ";
std::cout << std::endl;
}
}
private:
std::vector<std::unique_ptr<StudentModern>> students_;
};
int main() {
StudentDatabase db;
db.add_student("张三", {85, 92, 78});
db.add_student("李四", {90, 95, 88});
db.print_all();
return 0;
}
预期输出:
张三: 85 92 78
李四: 90 95 88
示例二:选择决策流程
#include <iostream>
#include <memory>
#include <vector>
// 场景 1:独占所有权 → unique_ptr
std::unique_ptr<int> create_unique() {
return std::make_unique<int>(42);
}
// 场景 2:共享所有权 → shared_ptr
void share_between_modules() {
auto sp = std::make_shared<int>(100);
std::vector<std::shared_ptr<int>> modules;
modules.push_back(sp); // 引用计数 = 2
modules.push_back(sp); // 引用计数 = 3
std::cout << "shared count: " << sp.use_count() << std::endl;
}
// 场景 3:不拥有 → 裸指针
void print_value(const int* ptr) {
if (ptr) std::cout << "value: " << *ptr << std::endl;
}
int main() {
auto u = create_unique();
print_value(u.get()); // 传递裸指针——不转移所有权
share_between_modules();
// 场景 4:容器代替手动数组
// ❌ int* arr = new int[100];
// ✅ std::vector<int> arr(100);
std::vector<int> arr(100);
std::cout << "vector size: " << arr.size() << std::endl;
return 0;
}
预期输出:
value: 42
shared count: 3
vector size: 100
最佳实践速查表
| 建议 | 说明 |
|---|---|
| 优先值语义 | 能用栈对象不用堆对象 |
unique_ptr 是默认选择 | 独占、零开销 |
避免裸 new/delete | 用 make_unique/make_shared |
| 用容器代替手动数组 | std::vector、std::array |
| 不拥有 → 裸指针或引用 | 函数参数、观察者 |
| Rule of Zero | 让编译器生成特殊成员函数 |
易错场景与面试考点
| 常见问题 | 参考答案 |
|---|---|
| 何时用 unique_ptr 而非 shared_ptr? | 默认用 unique_ptr,只在真正需要共享所有权时用 shared_ptr |
为什么避免 new? | make_unique 异常安全、代码更简洁 |
| Rule of Zero 是什么? | 如果不需要自定义析构/拷贝/移动,就不要写——交给编译器 |
小结
- 现代 C++ 内存管理:RAII + 智能指针 + 容器
- 默认选择:栈对象 > unique_ptr > shared_ptr
- 永远不要写裸
new/delete - Rule of Zero:让编译器做事,减少出错