Skip to content

内存管理

C++提供了手动管理内存的能力,这是C++强大但也容易出错的地方。本章将介绍动态内存分配、智能指针和内存管理最佳实践。

动态内存分配

cpp
#include <iostream>
using namespace std;

int main() {
    // ============ new和delete ============
    
    // 动态分配单个对象
    int* ptr = new int;       // 分配一个int
    *ptr = 100;
    cout << "*ptr = " << *ptr << endl;
    delete ptr;               // 释放
    ptr = nullptr;            // 置空,避免悬空指针
    
    // 初始化
    int* ptr2 = new int(42);  // 分配并初始化为42
    cout << "*ptr2 = " << *ptr2 << endl;
    delete ptr2;
    
    // C++11:列表初始化
    int* ptr3 = new int{100};
    cout << "*ptr3 = " << *ptr3 << endl;
    delete ptr3;
    
    
    // ============ 动态数组 ============
    
    // 分配数组
    int* arr = new int[5];    // 分配5个int
    
    // 初始化数组
    for (int i = 0; i < 5; i++) {
        arr[i] = i * 10;
    }
    
    cout << "\n动态数组: ";
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    delete[] arr;             // 释放数组(注意用delete[])
    
    // C++11:初始化列表
    int* arr2 = new int[5]{1, 2, 3, 4, 5};
    cout << "初始化数组: ";
    for (int i = 0; i < 5; i++) {
        cout << arr2[i] << " ";
    }
    cout << endl;
    delete[] arr2;
    
    
    // ============ 二维数组 ============
    
    // 分配二维数组
    int rows = 3, cols = 4;
    int** matrix = new int*[rows];
    
    for (int i = 0; i < rows; i++) {
        matrix[i] = new int[cols];
    }
    
    // 使用
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j;
        }
    }
    
    cout << "\n二维数组:" << endl;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            cout << matrix[i][j] << "\t";
        }
        cout << endl;
    }
    
    // 释放二维数组
    for (int i = 0; i < rows; i++) {
        delete[] matrix[i];
    }
    delete[] matrix;
    
    
    // ============ 处理分配失败 ============
    
    try {
        // 可能失败的分配
        // int* huge = new int[1000000000000];
    }
    catch (const bad_alloc& e) {
        cerr << "内存分配失败: " << e.what() << endl;
    }
    
    // nothrow版本(不抛出异常)
    int* safe = new(nothrow) int[1000];
    if (safe == nullptr) {
        cerr << "分配失败" << endl;
    } else {
        delete[] safe;
    }
    
    return 0;
}

智能指针详解

cpp
#include <iostream>
#include <memory>
using namespace std;

class Resource {
public:
    string name;
    Resource(string n) : name(n) {
        cout << "创建: " << name << endl;
    }
    ~Resource() {
        cout << "销毁: " << name << endl;
    }
    void use() {
        cout << "使用: " << name << endl;
    }
};

int main() {
    // ============ unique_ptr ============
    
    cout << "===== unique_ptr =====" << endl;
    
    // 创建方式
    unique_ptr<Resource> up1(new Resource("资源1"));
    auto up2 = make_unique<Resource>("资源2");  // 推荐
    
    up1->use();
    up2->use();
    
    // 独占所有权,不能复制
    // unique_ptr<Resource> up3 = up1;  // 错误
    
    // 可以移动
    unique_ptr<Resource> up3 = move(up1);
    if (up1 == nullptr) {
        cout << "up1已转移" << endl;
    }
    
    // 获取原始指针
    Resource* raw = up3.get();
    raw->use();
    
    // 释放所有权
    Resource* released = up3.release();
    delete released;  // 手动删除
    
    // 重置
    up2.reset();  // 释放并置空
    up2.reset(new Resource("新资源"));  // 释放旧的,指向新的
    
    
    // ============ shared_ptr ============
    
    cout << "\n===== shared_ptr =====" << endl;
    
    // 创建
    shared_ptr<Resource> sp1 = make_shared<Resource>("共享资源");
    
    cout << "引用计数: " << sp1.use_count() << endl;  // 1
    
    {
        shared_ptr<Resource> sp2 = sp1;  // 共享所有权
        cout << "引用计数: " << sp1.use_count() << endl;  // 2
        
        shared_ptr<Resource> sp3 = sp1;
        cout << "引用计数: " << sp1.use_count() << endl;  // 3
        
        sp2->use();
    }
    
    cout << "引用计数: " << sp1.use_count() << endl;  // 1
    
    // 自定义删除器
    shared_ptr<FILE> file(
        fopen("test.txt", "w"),
        [](FILE* f) {
            if (f) {
                fclose(f);
                cout << "文件已关闭" << endl;
            }
        }
    );
    
    
    // ============ weak_ptr ============
    
    cout << "\n===== weak_ptr =====" << endl;
    
    shared_ptr<Resource> shared = make_shared<Resource>("弱引用资源");
    weak_ptr<Resource> weak = shared;
    
    cout << "shared计数: " << shared.use_count() << endl;  // 1
    cout << "weak计数: " << weak.use_count() << endl;     // 1(不影响)
    
    // 使用weak_ptr需要lock
    if (auto locked = weak.lock()) {
        locked->use();
    }
    
    // 检查是否过期
    shared.reset();
    if (weak.expired()) {
        cout << "资源已释放" << endl;
    }
    
    
    // ============ 解决循环引用 ============
    
    cout << "\n===== 循环引用问题 =====" << endl;
    
    class Node {
    public:
        string name;
        shared_ptr<Node> next;      // 使用shared_ptr会导致循环引用
        weak_ptr<Node> prev;        // 使用weak_ptr打破循环
        
        Node(string n) : name(n) {
            cout << "创建节点: " << name << endl;
        }
        ~Node() {
            cout << "销毁节点: " << name << endl;
        }
    };
    
    {
        auto node1 = make_shared<Node>("节点1");
        auto node2 = make_shared<Node>("节点2");
        
        node1->next = node2;
        node2->prev = node1;  // 使用weak_ptr,不会增加引用计数
        
        cout << "离开作用域" << endl;
    }
    // 两个节点都会被正确销毁
    
    return 0;
}

内存管理最佳实践

cpp
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

// ============ RAII类 ============

class FileHandle {
private:
    FILE* file;
    
public:
    FileHandle(const char* filename, const char* mode) 
        : file(fopen(filename, mode)) {
        if (!file) {
            throw runtime_error("无法打开文件");
        }
    }
    
    ~FileHandle() {
        if (file) {
            fclose(file);
        }
    }
    
    // 禁止拷贝
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    
    // 允许移动
    FileHandle(FileHandle&& other) noexcept : file(other.file) {
        other.file = nullptr;
    }
    
    FileHandle& operator=(FileHandle&& other) noexcept {
        if (this != &other) {
            if (file) fclose(file);
            file = other.file;
            other.file = nullptr;
        }
        return *this;
    }
    
    FILE* get() const { return file; }
};

// ============ 动态数组类 ============

template<typename T>
class DynamicArray {
private:
    unique_ptr<T[]> data;
    size_t size_;
    size_t capacity_;
    
public:
    DynamicArray() : size_(0), capacity_(10) {
        data = make_unique<T[]>(capacity_);
    }
    
    void push_back(const T& value) {
        if (size_ >= capacity_) {
            // 扩容
            size_t newCapacity = capacity_ * 2;
            auto newData = make_unique<T[]>(newCapacity);
            
            for (size_t i = 0; i < size_; i++) {
                newData[i] = move(data[i]);
            }
            
            data = move(newData);
            capacity_ = newCapacity;
        }
        
        data[size_++] = value;
    }
    
    T& operator[](size_t index) {
        return data[index];
    }
    
    const T& operator[](size_t index) const {
        return data[index];
    }
    
    size_t size() const { return size_; }
    size_t capacity() const { return capacity_; }
    
    T* begin() { return data.get(); }
    T* end() { return data.get() + size_; }
};

int main() {
    // 使用RAII类
    try {
        FileHandle fh("test.txt", "w");
        fputs("Hello, RAII!", fh.get());
        // 自动关闭
    }
    catch (const exception& e) {
        cerr << e.what() << endl;
    }
    
    // 使用动态数组类
    DynamicArray<int> arr;
    
    for (int i = 0; i < 20; i++) {
        arr.push_back(i * 10);
    }
    
    cout << "\n动态数组: ";
    for (size_t i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    cout << "大小: " << arr.size() << ", 容量: " << arr.capacity() << endl;
    
    // 使用范围for
    cout << "范围for: ";
    for (int n : arr) {
        cout << n << " ";
    }
    cout << endl;
    
    return 0;
}

本章小结

本章学习了:

  • 动态内存分配:new、delete、new[]、delete[]
  • 智能指针:unique_ptr、shared_ptr、weak_ptr
  • RAII:资源获取即初始化
  • 最佳实践:避免内存泄漏、使用智能指针

下一章,我们将学习高级特性,了解C++11/14/17/20的新特性。