1. memorymanagementoverview
1.1 memory区域
in C++程序in, memory主要分 for 以 under 几个区域:
- code区 (Code Area) : store程序 可执行指令.
- 全局/静态区 (Global/Static Area) : store全局variable and 静态variable.
- 常量区 (Constant Area) : store常量, such asstring常量.
- 栈区 (Stack Area) : storefunctionparameter and 局部variable, 由编译器自动management.
- 堆区 (Heap Area) : 动态分配 memory, 由程序员手动management.
1.2 栈memory and 堆memory 比较
| features | 栈memory | 堆memory |
|---|---|---|
| 分配方式 | 编译器自动分配 | 程序员手动分配 |
| 释放方式 | function结束时自动释放 | 程序员手动释放 |
| 空间 big small | 较 small | 较 big |
| 分配速度 | fast | slow |
| memory碎片 | 无 | 可能产生 |
2. 动态memory分配
2.1 new and delete 运算符
in C++in, usingnew and delete运算符for动态memory分配 and 释放:
2.1.1 分配单个object
// 分配单个intclass型 memory int* p = new int; // 分配并初始化 int* q = new int(10); // 释放memory delete p; delete q;
2.1.2 分配array
// 分配array int* arr = new int[10]; // 释放array delete[] arr;
2.1.3 分配object
class Person {
public:
Person() { cout << "constructfunction" << endl; }
~Person() { cout << "析构function" << endl; }
};
// 分配object
Person* p = new Person;
// 释放object
delete p;
2.2 memory分配失败
当memory分配失败时, new运算符会抛出std::bad_allocexception. in C++11in, 可以usingnew(nothrow)来避免抛出exception, 而 is 返回nullptr:
// 可能抛出exception
int* p1 = new int[1000000000];
// 不会抛出exception, 失败时返回nullptr
int* p2 = new(nothrow) int[1000000000];
if (p2 == nullptr) {
cout << "memory分配失败" << endl;
}
delete[] p1;
delete[] p2;
3. 智能指针
3.1 智能指针 concepts
智能指针 is C++标准libraryproviding 一种RAII (resource获取即初始化) 风格 指针package装器, 它可以自动management动态分配 memory, 避免memory泄漏.
C++11引入了三种智能指针:
- std::unique_ptr: 独占所 has 权 智能指针
- std::shared_ptr: 共享所 has 权 智能指针
- std::weak_ptr: 不增加引用计数 智能指针, 用于解决shared_ptr 循环引用issues
3.2 std::unique_ptr
std::unique_ptr is a独占所 has 权 智能指针, 同一时间只能 has 一个unique_ptr指向同一个object:
#include <iostream>
#include <memory>
using namespace std;
class Person {
public:
Person(string name) : name(name) {
cout << "Person " << name << " construct" << endl;
}
~Person() {
cout << "Person " << name << " 析构" << endl;
}
void sayHello() {
cout << "Hello, I'm " << name << endl;
}
private:
string name;
};
int main() {
// creationunique_ptr
unique_ptr p1(new Person("Alice"));
p1->sayHello();
// 转移所 has 权
unique_ptr p2 = move(p1);
// p1现 in for 空
if (p1) {
p1->sayHello();
} else {
cout << "p1 is null" << endl;
}
p2->sayHello();
// 不需要手动delete, 离开作用域时自动释放
return 0;
}
3.3 std::shared_ptr
std::shared_ptr is a共享所 has 权 智能指针, many 个shared_ptr可以指向同一个object, using引用计数来managementobject 生命周期:
#include <iostream>
#include <memory>
using namespace std;
class Person {
public:
Person(string name) : name(name) {
cout << "Person " << name << " construct" << endl;
}
~Person() {
cout << "Person " << name << " 析构" << endl;
}
void sayHello() {
cout << "Hello, I'm " << name << endl;
}
private:
string name;
};
int main() {
// creationshared_ptr
shared_ptr p1(new Person("Bob"));
cout << "引用计数: " << p1.use_count() << endl;
// copyshared_ptr, 引用计数增加
shared_ptr p2 = p1;
cout << "引用计数: " << p1.use_count() << endl;
// 再次copy
shared_ptr p3 = p2;
cout << "引用计数: " << p1.use_count() << endl;
p1->sayHello();
p2->sayHello();
p3->sayHello();
// 离开作用域时, 引用计数减 for 0, 自动释放object
return 0;
}
3.4 std::weak_ptr
std::weak_ptr is a不增加引用计数 智能指针, 用于解决shared_ptr 循环引用issues:
#include <iostream>
#include <memory>
using namespace std;
class B;
class A {
public:
shared_ptr b_ptr;
~A() {
cout << "A 析构" << endl;
}
};
class B {
public:
weak_ptr a_ptr; // usingweak_ptr避免循环引用
~B() {
cout << "B 析构" << endl;
}
};
int main() {
shared_ptr a(new A());
shared_ptr b(new B());
a->b_ptr = b;
b->a_ptr = a;
// checkweak_ptr is 否 has 效
if (auto pa = b->a_ptr.lock()) {
cout << "weak_ptr has 效" << endl;
}
return 0;
// 离开作用域时, a and b都能正确析构
}
4. memory泄漏
4.1 what is memory泄漏?
memory泄漏 is 指程序in动态分配 memory没 has 被正确释放, 导致这些memory无法被再次using, 直 to 程序结束.
memory泄漏 危害:
- 程序占用 memory逐渐增加, 可能导致systemmemory不足
- 程序performance under 降
- in long 时间run 程序in, 可能导致程序崩溃
4.2 common memory泄漏场景
4.2.1 忘记释放memory
void func() {
int* p = new int[100];
// 忘记释放p
}
int main() {
func();
// memory泄漏
return 0;
}
4.2.2 exception导致 memory泄漏
void func() {
int* p = new int[100];
// exception发生, delete不会执行
throw runtime_error("error");
delete[] p;
}
int main() {
try {
func();
} catch (const exception& e) {
cout << e.what() << endl;
}
// memory泄漏
return 0;
}
4.2.3 循环引用
class B;
class A {
public:
shared_ptr b_ptr;
~A() {
cout << "A 析构" << endl;
}
};
class B {
public:
shared_ptr a_ptr; // 这里usingshared_ptr导致循环引用
~B() {
cout << "B 析构" << endl;
}
};
int main() {
{
shared_ptr a(new A());
shared_ptr b(new B());
a->b_ptr = b;
b->a_ptr = a;
}
// 离开作用域 after , a and b 引用计数都 for 1, 不会析构, 导致memory泄漏
return 0;
}
4.3 such as何避免memory泄漏
- using智能指针: 优先usingstd::unique_ptr and std::shared_ptr来management动态memory.
- 遵循RAIIprinciples: 将resource 获取 and object 初始化绑定, 将resource 释放 and object 销毁绑定.
- usingcontainersclass: usingSTLcontainerssuch asvector, listetc., 它们会自动managementmemory.
- 避免裸指针: 尽量避免using裸指针management动态memory.
- usingmemory泄漏检测tool: such asValgrind, Dr. Memoryetc.tool来检测memory泄漏.
5. memorymanagement best practices
5.1 commonprinciples
- 优先using栈memory: for 于生命周期 short , big small 固定 object, 优先using栈memory.
- 合理using堆memory: for 于生命周期 long , big small 不固定 object, using堆memory.
- 及时释放memory: using裸指针时, 确保每个new都 has for 应 delete.
- using智能指针: 尽量using智能指针来management堆memory.
- 避免memory碎片: 尽量分配 big small 合理 memory块, 避免频繁分配 and 释放 small memory块.
5.2 智能指针 using建议
- 优先usingstd::unique_ptr: 当object所 has 权唯一时, usingstd::unique_ptr.
- 需要共享所 has 权时usingstd::shared_ptr: 当 many 个指针需要共享object所 has 权时, usingstd::shared_ptr.
- usingstd::weak_ptr解决循环引用: 当存 in 循环引用时, usingstd::weak_ptr.
- usingstd::make_unique and std::make_shared: 优先using这些function来creation智能指针, 而不 is 直接usingnew.
5.3 memoryoptimizationtechniques
- memory池: for 于频繁分配 and 释放 small memory块, usingmemory池可以reducingmemory碎片 and improvingperformance.
- object池: for 于频繁creation and 销毁 object, usingobject池可以reducingconstruct and 析构 开销.
- move语义: usingmove语义 (C++11及以 after ) 可以reducing不必要 memory拷贝.
- memory for 齐: 合理 memory for 齐可以improvingmemory访问efficiency.
实践case: 智能指针managementresource
writing一个C++程序, using智能指针来managementfileresource, 确保file in 任何circumstances under 都能被正确关闭.
requirementsanalysis
- creation一个
Fileprocessingrclass, 用于managementfileresource. - using智能指针来management
Fileprocessingrobject. - 确保 in exception发生时, file也能被正确关闭.
referencecode
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
using namespace std;
class Fileprocessingr {
private:
fstream file;
string filename;
public:
Fileprocessingr(const string& name, ios_base::openmode mode = ios_base::in) {
filename = name;
file.open(name, mode);
if (!file.is_open()) {
throw runtime_error("无法打开file: " + name);
}
cout << "file " << name << " 打开" << endl;
}
~Fileprocessingr() {
if (file.is_open()) {
file.close();
cout << "file " << filename << " 关闭" << endl;
}
}
void write(const string& content) {
if (file.is_open()) {
file << content << endl;
}
}
string readLine() {
string line;
if (file.is_open() && getline(file, line)) {
return line;
}
return "";
}
bool isOpen() const {
return file.is_open();
}
};
void processFile() {
// usingunique_ptrmanagementFileprocessingr
unique_ptr filePtr(new Fileprocessingr("test.txt", ios_base::out | ios_base::trunc));
// 写入data
filePtr->write("Hello, World!");
filePtr->write("智能指针test");
// mockexception
throw runtime_error("processingfile时发生error");
// 即使发生exception, filePtr离开作用域时也会自动释放, 调用Fileprocessingr 析构function关闭file
}
int main() {
try {
processFile();
} catch (const exception& e) {
cout << "捕获 to exception: " << e.what() << endl;
}
// verificationfile is 否被正确关闭
cout << "程序继续执行" << endl;
// 读取file in 容
try {
unique_ptr filePtr(new Fileprocessingr("test.txt"));
string line;
while (!filePtr->readLine().empty()) {
cout << "读取 to : " << line << endl;
line = filePtr->readLine();
}
} catch (const exception& e) {
cout << "读取file时发生error: " << e.what() << endl;
}
return 0;
}
run结果
file test.txt 打开 file test.txt 关闭 捕获 to exception: processingfile时发生error 程序继续执行 file test.txt 打开 读取 to : Hello, World! 读取 to : 智能指针test file test.txt 关闭
互动练习
练习1: writing一个C++程序, usingstd::unique_ptrmanagement动态分配 array.
练习2: writing一个C++程序, usingstd::shared_ptrmanagement一个 big 型object, 并演示 many 个shared_ptr共享同一个object circumstances.
练习3: modify实践casein Fileprocessingrclass, 添加更 many fileoperationfunctions, such as读取整个file, 定位file指针etc..