12C++ Primer(第5版)

动态内存

内存管理

new/delete智能指针动态数组

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第12章:动态内存(Dynamic Memory)

一、导读

1.1 本章学习目标

第12章讲解C++的动态内存管理。本章核心目标:

  • 掌握new和delete:动态内存分配和释放
  • 理解智能指针:shared_ptr、unique_ptr、weak_ptr
  • 学会使用make_shared和make_unique
  • 理解内存泄漏和悬空指针
  • 掌握动态数组与vector的比较

1.2 为什么本章如此重要

动态内存管理是C++中最容易出错的部分。内存泄漏、悬空指针、重复释放——这些问题在大型程序中可能导致灾难性后果。C++11引入的智能指针大大简化了内存管理,是现代C++编程的重要特性。

1.3 核心问题引导

new和malloc有什么区别?

shared_ptr的引用计数是如何工作的?

unique_ptr和shared_ptr各适合什么场景?

什么是循环引用?weak_ptr如何解决?


二、核心概念详解

12.1 new和delete

cpp
#include <iostream>
#include <string>

int main() {
    // 单个对象
    int *p1 = new int(42);
    std::cout << *p1 << std::endl;
    delete p1;
    
    // 动态数组
    int *arr = new int[5]{1, 2, 3, 4, 5};
    for (int i = 0; i < 5; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    delete[] arr;  // 数组必须用delete[]
    
    // 对象
    std::string *sp = new std::string("Hello");
    std::cout << *sp << std::endl;
    delete sp;
    
    // new失败时抛出bad_alloc异常
    try {
        int *huge = new int[1000000000000];
    } catch (const std::bad_alloc &e) {
        std::cerr << "Allocation failed: " << e.what() << std::endl;
    }
    
    // nothrow版本
    int *p = new (std::nothrow) int[1000000000000];
    if (!p) {
        std::cerr << "Allocation failed" << std::endl;
    }
    
    return 0;
}

12.2 智能指针

cpp
#include <iostream>
#include <memory>
#include <string>
#include <vector>

class Student {
    std::string name;
    int age;
public:
    Student(std::string n, int a) : name(n), age(a) {
        std::cout << "Constructor: " << name << std::endl;
    }
    ~Student() {
        std::cout << "Destructor: " << name << std::endl;
    }
    void display() const {
        std::cout << name << ", " << age << std::endl;
    }
};

int main() {
    // shared_ptr:共享所有权,引用计数
    auto sp1 = std::make_shared<Student>("Alice", 20);
    sp1->display();
    
    {
        auto sp2 = sp1;  // 引用计数+1
        std::cout << "Count: " << sp1.use_count() << std::endl;  // 2
    }
    // sp2离开作用域,引用计数-1
    std::cout << "Count: " << sp1.use_count() << std::endl;  // 1
    
    // unique_ptr:独占所有权,不能复制
    auto up1 = std::make_unique<Student>("Bob", 21);
    // auto up2 = up1;  // 错误:不能复制
    auto up2 = std::move(up1);  // 转移所有权
    // up1现在为空
    
    // weak_ptr:不增加引用计数,解决循环引用
    std::shared_ptr<Student> sp3 = std::make_shared<Student>("Charlie", 19);
    std::weak_ptr<Student> wp = sp3;
    
    std::cout << "sp3 count: " << sp3.use_count() << std::endl;  // 1
    std::cout << "wp expired: " << wp.expired() << std::endl;    // 0
    
    if (auto locked = wp.lock()) {
        locked->display();
    }
    
    // 动态对象数组
    std::vector<std::shared_ptr<Student>> students;
    students.push_back(std::make_shared<Student>("David", 22));
    students.push_back(std::make_shared<Student>("Eve", 20));
    
    for (const auto &s : students) {
        s->display();
    }
    
    return 0;
}

12.3 循环引用和weak_ptr

cpp
#include <iostream>
#include <memory>

class Node {
public:
    std::string name;
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // 使用weak_ptr避免循环引用
    
    Node(std::string n) : name(n) {
        std::cout << "Node created: " << name << std::endl;
    }
    ~Node() {
        std::cout << "Node destroyed: " << name << std::endl;
    }
};

int main() {
    auto node1 = std::make_shared<Node>("Node1");
    auto node2 = std::make_shared<Node>("Node2");
    
    node1->next = node2;
    node2->prev = node1;  // weak_ptr不增加引用计数
    
    std::cout << "node1 count: " << node1.use_count() << std::endl;  // 1
    std::cout << "node2 count: " << node2.use_count() << std::endl;  // 2
    
    // 如果使用shared_ptr作为prev,会导致循环引用
    // node1和node2永远不会被销毁
    
    return 0;
}

12.4 allocator类

cpp
#include <iostream>
#include <memory>
#include <string>
#include <vector>

int main() {
    // allocator:分离内存分配和对象构造
    std::allocator<std::string> alloc;
    
    // 分配未初始化的内存
    auto const p = alloc.allocate(3);
    
    // 在分配的内存中构造对象
    auto q = p;
    alloc.construct(q++, "Hello");
    alloc.construct(q++, "World");
    alloc.construct(q++, "!");
    
    // 使用
    for (auto it = p; it != q; ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 析构对象
    while (q != p) {
        alloc.destroy(--q);
    }
    
    // 释放内存
    alloc.deallocate(p, 3);
    
    // 使用uninitialized_copy
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::allocator<int> int_alloc;
    auto pi = int_alloc.allocate(5);
    std::uninitialized_copy(v.begin(), v.end(), pi);
    
    for (int i = 0; i < 5; i++) {
        std::cout << pi[i] << " ";
    }
    std::cout << std::endl;
    
    int_alloc.deallocate(pi, 5);
    
    return 0;
}

三、实战案例

3.1 对象池

cpp
#include <iostream>
#include <memory>
#include <vector>
#include <stack>

class Object {
    int id;
    static int next_id;
public:
    Object() : id(next_id++) {
        std::cout << "Object " << id << " created" << std::endl;
    }
    ~Object() {
        std::cout << "Object " << id << " destroyed" << std::endl;
    }
    void reset() { /* 重置状态 */ }
    int getId() const { return id; }
};

int Object::next_id = 0;

class ObjectPool {
    std::stack<std::shared_ptr<Object>> pool;
    size_t max_size;
public:
    ObjectPool(size_t size) : max_size(size) {
        for (size_t i = 0; i < size; i++) {
            pool.push(std::make_shared<Object>());
        }
    }
    
    std::shared_ptr<Object> acquire() {
        if (pool.empty()) {
            throw std::runtime_error("Pool exhausted");
        }
        auto obj = pool.top();
        pool.pop();
        return obj;
    }
    
    void release(std::shared_ptr<Object> obj) {
        obj->reset();
        pool.push(obj);
    }
    
    size_t available() const { return pool.size(); }
};

int main() {
    ObjectPool pool(3);
    std::cout << "Available: " << pool.available() << std::endl;
    
    auto obj1 = pool.acquire();
    auto obj2 = pool.acquire();
    std::cout << "Available: " << pool.available() << std::endl;
    
    pool.release(obj1);
    std::cout << "Available: " << pool.available() << std::endl;
    
    return 0;
}

四、本章小结

第12章介绍了动态内存管理:new/delete、智能指针(shared_ptr、unique_ptr、weak_ptr)、循环引用问题、allocator类。现代C++推荐使用智能指针管理动态内存。


五、思考与练习

思考题

shared_ptr的引用计数是线程安全的吗?

unique_ptr的自定义删除器如何使用?

如何检测内存泄漏?

什么时候应该用new/delete,什么时候用智能指针?

编程练习

实现一个简单的智能指针类。

使用shared_ptr实现一个观察者模式。

实现一个内存池,管理固定大小的对象。


12/12动态内存
0%