C++memorymanagementtutorial

LearningC++in memorymanagementmechanism, including堆memory, 栈memory, 智能指针etc.

返回tutoriallist

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:

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 循环引用

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智能指针来managementFileprocessingrobject.
  • 确保 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..