智能指针与内存

翻译整理自 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 容器 是最佳实践。