智能指针与内存
翻译整理自 cppreference - 内存。
1. 原始指针与 new/delete
int* p = new int(42);
int* arr = new int[10];
delete p;
delete[] arr;现代 C++ 几乎不再直接用 new/delete,改用智能指针与容器。
2. unique_ptr
独占所有权,不可拷贝、可移动:
#include <memory>
std::unique_ptr<int> p = std::make_unique<int>(42);
std::unique_ptr<int[]> a = std::make_unique<int[]>(10);
*p; a[0];
std::unique_ptr<int> q = std::move(p); // 转移所有权
// p 此时为 nullptr
自定义删除器:
std::unique_ptr<FILE, decltype(&std::fclose)> fp(std::fopen("x", "r"), &std::fclose);3. shared_ptr
引用计数共享所有权:
auto p = std::make_shared<int>(42);
auto q = p; // 引用计数 2
p.use_count(); // 2
make_shared一次分配,更高效。
控制块开销:原子计数 + weak 计数,比 unique_ptr 重。
4. weak_ptr
打破循环引用:
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 用 weak 避免循环引用
};
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->next = b;
b->prev = a;锁定 weak_ptr:
std::weak_ptr<int> w = sptr;
if (auto p = w.lock()) { // 提升 shared_ptr
use(*p);
}5. RAII
资源获取即初始化:在构造函数中获取,在析构函数中释放。
class Locker {
public:
Locker(std::mutex& m) : m_(m) { m_.lock(); }
~Locker() { m_.unlock(); }
private:
std::mutex& m_;
};
std::mutex mtx;
{
Locker lock(mtx);
// ...
}6. 标准库锁
#include <mutex>
std::mutex mtx;
{
std::lock_guard<std::mutex> lock(mtx); // 进入锁,析构解锁
// ...
}
std::recursive_mutex rmtx;
std::unique_lock<std::mutex> ul(mtx); // 可解锁再锁
ul.unlock();
ul.lock();C++17 起可简化:
std::mutex a, b;
std::scoped_lock lock(a, b); // 同时锁多个,避免死锁
std::lock_guard lock(mtx); // 模板参数可省略
7. 移动语义
std::vector<int> v1(1000);
std::vector<int> v2 = std::move(v1); // 移动,O(1)
实现自定义移动构造:
class Buffer {
public:
Buffer(Buffer&& o) noexcept
: data_(o.data_), size_(o.size_) {
o.data_ = nullptr;
o.size_ = 0;
}
Buffer& operator=(Buffer&& o) noexcept {
if (this != &o) {
delete[] data_;
data_ = o.data_; size_ = o.size_;
o.data_ = nullptr; o.size_ = 0;
}
return *this;
}
private:
char* data_ = nullptr;
size_t size_ = 0;
};8. 完美转发
template <typename T>
void wrapper(T&& arg) {
target(std::forward<T>(arg)); // 保持 lvalue/rvalue 性质
}T&& 在模板中为"转发引用"(forwarding reference),std::forward 恢复左右值。
9. 内存对齐与未对齐访问
#include <new>
alignas(16) char buf[16]; // 强制对齐
auto aligned = std::launder(reinterpret_cast<int*>(buf));10. 常用工具
std::addressof:取对象地址(即使重载了operator&)std::launder:在placement new 后访问指针std::byte:类型安全的字节类型<memory>还提供allocator、pointer_traits
小结
现代 C++ 几乎不需要手动管理内存,RAII + 智能指针 + STL 容器 是最佳实践。