React副作用Hook详解:useEffect、useLayoutEffect与useInsertionEffect
2026/9/16 10:58:56
好的,我们来详细讲解 C++ 中的内存管理。理解 C++ 的内存管理机制对于编写高效、安全、无内存泄漏的程序至关重要。
1. 内存区域概述
C++ 程序在运行时使用的内存通常被划分为几个关键区域:
new)在堆上申请内存,并在不再需要时通过对应的操作(如delete)释放内存。如果管理不当(如忘记释放),会导致内存泄漏。2. 动态内存分配 (new和delete)
这是 C++ 管理堆内存的核心机制。
new运算符:用于在堆上分配内存。int *ptr = new int;// 分配一个 int 大小的内存,并返回指向它的指针int *arr = new int[10];// 分配一个包含 10 个 int 的数组,返回指向第一个元素的指针int *ptr = new int(42);// 分配内存并初始化为 42new在分配失败时会抛出std::bad_alloc异常(除非使用nothrow版本)。delete运算符:用于释放由new分配的内存。delete ptr;//ptr必须是指向由new分配的单个对象的指针delete[] arr;//arr必须是指向由new[]分配的数组的指针new和delete,以及new[]和delete[]。错误配对(如用delete释放数组或用delete[]释放单个对象)会导致未定义行为,通常是程序崩溃。malloc和free(C 风格):C++ 兼容 C 的内存管理函数,但不推荐在 C++ 中优先使用,因为:malloc只分配内存,不调用构造函数。free只释放内存,不调用析构函数。3. 内存泄漏 (Memory Leak)
内存泄漏是指程序在堆上分配了内存,但之后失去了对这块内存的引用(指针丢失),且未能释放它。后果是:
常见泄漏场景:
delete或delete[]。int* ptr = new int; ptr = new int; // 第一个 new 分配的内存泄漏了!delete未执行。try { int* ptr = new int; // ... 可能抛出异常的代码 ... delete ptr; } catch (...) { // 如果异常发生在 delete 之前,ptr 指向的内存泄漏 }4. 智能指针 (Smart Pointers - C++11 及以后)
为了解决手动管理内存(new/delete)的复杂性和易出错性(尤其是内存泄漏和异常安全问题),C++11 引入了智能指针。它们在<memory>头文件中定义。
std::unique_ptr:表示对动态分配对象的独占所有权。unique_ptr可以指向该对象。unique_ptr被销毁(如离开作用域)时,它所指向的对象会被自动删除。#include <memory> { std::unique_ptr<int> uptr(new int(10)); // 创建并管理一个 int // 使用 *uptr 访问 } // uptr 离开作用域,自动调用 delete 释放内存std::shared_ptr:表示对动态分配对象的共享所有权。shared_ptr可以指向同一个对象。shared_ptr被销毁或重置时,对象才会被删除。#include <memory> { std::shared_ptr<int> sptr1 = std::make_shared<int>(20); // 更好,效率更高且更安全 { std::shared_ptr<int> sptr2 = sptr1; // 拷贝,引用计数+1 // 两者都指向同一个 int } // sptr2 销毁,引用计数-1 } // sptr1 销毁,引用计数变为0,自动删除对象std::weak_ptr:是shared_ptr的辅助指针,不增加引用计数。shared_ptr的循环引用问题(两个或多个shared_ptr互相引用导致对象无法释放)。lock()方法尝试提升为shared_ptr(如果对象还存在)。#include <memory> std::shared_ptr<int> sptr = std::make_shared<int>(30); std::weak_ptr<int> wptr = sptr; // 创建 weak_ptr 指向同一个对象 { std::shared_ptr<int> sptr2 = wptr.lock(); // 尝试提升,如果对象还在,sptr2 有效 if (sptr2) { // 安全地使用 sptr2 } } // sptr2 销毁 ``` // sptr 销毁后,wptr.lock() 会返回空 shared_ptrstd::make_unique(C++14) 和std::make_shared(C++11):
new(如果后续代码抛出异常,new的结果没有被智能指针捕获,可能泄漏)。make_shared):可能将对象和控制块(存储引用计数等信息)分配在连续的内存区域。5. 总结与最佳实践
new和delete来管理对象的生命周期。这是现代 C++ 的核心建议。unique_ptr。简单、高效。shared_ptr和weak_ptr。注意避免循环引用。make_unique和make_shared来创建它们管理的对象。示例:综合运用
#include <iostream> #include <memory> #include <vector> class MyClass { public: MyClass(int v) : value(v) { std::cout << "Constructing " << value << std::endl; } ~MyClass() { std::cout << "Destructing " << value << std::endl; } void print() { std::cout << "Value: " << value << std::endl; } private: int value; }; int main() { // 栈上对象 - 自动管理 MyClass stackObj(1); // unique_ptr 管理单个对象 std::unique_ptr<MyClass> uptr = std::make_unique<MyClass>(2); // shared_ptr 管理共享对象 std::shared_ptr<MyClass> sptr1 = std::make_shared<MyClass>(3); { std::shared_ptr<MyClass> sptr2 = sptr1; // 共享 sptr2->print(); // 使用箭头运算符访问成员 } // sptr2 析构,对象还在(sptr1 还在) // 容器存储智能指针 std::vector<std::unique_ptr<MyClass>> vec; vec.push_back(std::make_unique<MyClass>(4)); vec.push_back(std::make_unique<MyClass>(5)); // 访问容器中的元素 vec[0]->print(); return 0; // 离开 main 作用域: // stackObj 析构 // uptr 析构 -> 其管理的对象 (2) 析构 // sptr1 析构 -> 引用计数归零 -> 对象 (3) 析构 // vec 析构 -> 其包含的两个 unique_ptr 析构 -> 对象 (4) 和 (5) 析构 } /* 输出示例: Constructing 1 Constructing 2 Constructing 3 Value: 3 Constructing 4 Constructing 5 Value: 4 Destructing 1 Destructing 5 Destructing 4 Destructing 3 Destructing 2 */这个示例展示了栈对象、unique_ptr、shared_ptr以及如何在容器中使用智能指针。注意析构的顺序(栈对象按创建相反顺序析构,智能指针管理的对象在其指针析构时析构)。通过使用智能指针,我们完全避免了显式的new和delete,大大降低了内存泄漏的风险。