C++智能指针【笔记】

1. 智能指针的设计哲学

RAII(资源获取即初始化)原则

cpp 复制代码
// 传统资源管理的痛点
void risky_function() {
    int* raw_ptr = new int(100);
    
    if (some_condition()) {
        delete raw_ptr;  // 容易忘记释放
        return;
    }
    
    throw std::runtime_error("错误");  // 异常导致内存泄漏
    delete raw_ptr;  // 永远不会执行
}

// RAII解决方案
void safe_function() {
    std::unique_ptr<int> smart_ptr = std::make_unique<int>(100);
    // 无论函数如何退出,资源都会自动释放
}

2. unique_ptr 深度解析

独占所有权的实现原理

cpp 复制代码
template<typename T>
class SimplifiedUniquePtr {
private:
    T* ptr_;
    
public:
    // 构造函数
    explicit SimplifiedUniquePtr(T* ptr = nullptr) : ptr_(ptr) {}
    
    // 禁止拷贝
    SimplifiedUniquePtr(const SimplifiedUniquePtr&) = delete;
    SimplifiedUniquePtr& operator=(const SimplifiedUniquePtr&) = delete;
    
    // 允许移动
    SimplifiedUniquePtr(SimplifiedUniquePtr&& other) noexcept 
        : ptr_(other.ptr_) {
        other.ptr_ = nullptr;
    }
    
    SimplifiedUniquePtr& operator=(SimplifiedUniquePtr&& other) noexcept {
        if (this != &other) {
            delete ptr_;
            ptr_ = other.ptr_;
            other.ptr_ = nullptr;
        }
        return *this;
    }
    
    // 析构函数
    ~SimplifiedUniquePtr() {
        delete ptr_;
    }
    
    // 操作符重载
    T& operator*() const { return *ptr_; }
    T* operator->() const { return ptr_; }
    explicit operator bool() const { return ptr_ != nullptr; }
};

高级用法

cpp 复制代码
#include <memory>

// 1. 自定义删除器
struct FileDeleter {
    void operator()(FILE* file) const {
        if (file) {
            fclose(file);
            std::cout << "文件资源已释放\n";
        }
    }
};

std::unique_ptr<FILE, FileDeleter> file_ptr(fopen("data.txt", "r"));

// 2. 工厂模式返回unique_ptr
class Widget {
private:
    Widget() = default;  // 私有构造函数
    friend class WidgetFactory;
};

class WidgetFactory {
public:
    static std::unique_ptr<Widget> create() {
        return std::unique_ptr<Widget>(new Widget());
    }
};

// 3. 数组特化
std::unique_ptr<int[]> array_ptr = std::make_unique<int[]>(100);
array_ptr[0] = 42;  // 直接使用数组语法

// 4. 释放所有权
auto ptr = std::make_unique<int>(100);
int* raw_ptr = ptr.release();  // unique_ptr不再管理该内存
// 必须手动管理 raw_ptr
delete raw_ptr;

3. shared_ptr 深度解析

引用计数实现原理

cpp 复制代码
template<typename T>
class SimplifiedSharedPtr {
private:
    T* ptr_;
    size_t* ref_count_;
    
    void cleanup() {
        if (ref_count_ && --(*ref_count_) == 0) {
            delete ptr_;
            delete ref_count_;
        }
    }
    
public:
    // 构造函数
    explicit SimplifiedSharedPtr(T* ptr = nullptr) 
        : ptr_(ptr), ref_count_(ptr ? new size_t(1) : nullptr) {}
    
    // 拷贝构造函数
    SimplifiedSharedPtr(const SimplifiedSharedPtr& other) 
        : ptr_(other.ptr_), ref_count_(other.ref_count_) {
        if (ref_count_) ++(*ref_count_);
    }
    
    // 拷贝赋值
    SimplifiedSharedPtr& operator=(const SimplifiedSharedPtr& other) {
        if (this != &other) {
            cleanup();
            ptr_ = other.ptr_;
            ref_count_ = other.ref_count_;
            if (ref_count_) ++(*ref_count_);
        }
        return *this;
    }
    
    // 移动语义
    SimplifiedSharedPtr(SimplifiedSharedPtr&& other) noexcept 
        : ptr_(other.ptr_), ref_count_(other.ref_count_) {
        other.ptr_ = nullptr;
        other.ref_count_ = nullptr;
    }
    
    ~SimplifiedSharedPtr() {
        cleanup();
    }
    
    size_t use_count() const { 
        return ref_count_ ? *ref_count_ : 0; 
    }
};

控制块与内存布局

cpp 复制代码
// make_shared 的内存优化
auto ptr1 = std::shared_ptr<int>(new int(42));  // 两次分配:对象+控制块
auto ptr2 = std::make_shared<int>(42);          // 一次分配:对象和控制块连续

// 控制块包含的信息:
// - 引用计数
// - 弱引用计数  
// - 自定义删除器
// - 分配器

循环引用问题详解

cpp 复制代码
struct BadNode {
    std::shared_ptr<BadNode> next;
    std::shared_ptr<BadNode> prev;
    
    ~BadNode() { std::cout << "Node销毁\n"; }  // 永远不会执行
};

void memory_leak_demo() {
    auto node1 = std::make_shared<BadNode>();
    auto node2 = std::make_shared<BadNode>();
    
    node1->next = node2;  // 引用计数: node1=2, node2=1
    node2->prev = node1;  // 引用计数: node1=2, node2=2
    
    // 离开作用域:引用计数变为1,内存泄漏!
}

// 解决方案:使用weak_ptr
struct GoodNode {
    std::shared_ptr<GoodNode> next;
    std::weak_ptr<GoodNode> prev;  // 弱引用,不增加计数
    
    ~GoodNode() { std::cout << "Node销毁\n"; }  // 正常执行
};

4. weak_ptr 深度解析

弱引用的实现机制

cpp 复制代码
// weak_ptr 不参与引用计数,但能感知对象是否存活
std::shared_ptr<int> shared = std::make_shared<int>(100);
std::weak_ptr<int> weak = shared;

// 使用前必须"锁定"
if (auto locked = weak.lock()) {
    std::cout << "对象存活,值: " << *locked << std::endl;
} else {
    std::cout << "对象已被销毁\n";
}

// expired() 快速检查
if (!weak.expired()) {
    auto locked = weak.lock();  // 安全使用
}

典型应用场景

cpp 复制代码
// 1. 观察者模式
class Subject {
private:
    std::vector<std::weak_ptr<Observer>> observers_;
    
public:
    void add_observer(std::weak_ptr<Observer> obs) {
        observers_.push_back(obs);
    }
    
    void notify() {
        for (auto it = observers_.begin(); it != observers_.end(); ) {
            if (auto obs = it->lock()) {
                obs->update();
                ++it;
            } else {
                it = observers_.erase(it);  // 清理失效的观察者
            }
        }
    }
};

// 2. 缓存系统
class Cache {
private:
    std::unordered_map<std::string, std::weak_ptr<LargeObject>> cache_;
    
public:
    std::shared_ptr<LargeObject> get(const std::string& key) {
        auto it = cache_.find(key);
        if (it != cache_.end()) {
            if (auto cached = it->second.lock()) {
                return cached;  // 对象仍在内存中
            } else {
                cache_.erase(it);  // 对象已被销毁
            }
        }
        return load_and_cache(key);
    }
};

5. 性能分析与优化

性能对比

cpp 复制代码
#include <chrono>

void benchmark() {
    const int iterations = 1000000;
    
    // 原始指针
    auto start1 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        int* ptr = new int(i);
        delete ptr;
    }
    auto end1 = std::chrono::high_resolution_clock::now();
    
    // unique_ptr
    auto start2 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        auto ptr = std::make_unique<int>(i);
    }
    auto end2 = std::chrono::high_resolution_clock::now();
    
    // shared_ptr
    auto start3 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        auto ptr = std::make_shared<int>(i);
    }
    auto end3 = std::chrono::high_resolution_clock::now();
    
    // 输出结果...
}

内存优化技巧

cpp 复制代码
// 1. 使用make_shared减少内存分配次数
auto bad_ptr = std::shared_ptr<Widget>(new Widget());  // 2次分配
auto good_ptr = std::make_shared<Widget>();            // 1次分配

// 2. 避免不必要的shared_ptr拷贝
void process_widget(std::shared_ptr<Widget> widget);  // 按值传递,增加计数

// 优化方案:
void process_widget(const std::shared_ptr<Widget>& widget);  // 按引用传递
void process_widget(Widget* widget);                        // 使用原始指针

// 3. 使用std::move转移所有权
std::vector<std::unique_ptr<Widget>> widgets;
auto widget = std::make_unique<Widget>();
widgets.push_back(std::move(widget));  // 高效的所有权转移

6. 高级特性与自定义

自定义分配器

cpp 复制代码
template<typename T>
class PoolAllocator {
    // 实现内存池分配器
};

class Widget {
public:
    static void* operator new(size_t size) {
        return pool_allocator.allocate(size);
    }
    
    static void operator delete(void* ptr) {
        pool_allocator.deallocate(static_cast<Widget*>(ptr));
    }
    
private:
    static PoolAllocator<Widget> pool_allocator;
};

// 使用自定义分配器的智能指针
auto widget_ptr = std::allocate_shared<Widget>(PoolAllocator<Widget>());

类型擦除删除器

cpp 复制代码
class AnyDeleter {
private:
    struct Concept {
        virtual ~Concept() = default;
        virtual void destroy(void*) = 0;
    };
    
    template<typename T, typename Deleter>
    struct Model : Concept {
        Deleter deleter;
        
        Model(Deleter d) : deleter(std::move(d)) {}
        
        void destroy(void* ptr) override {
            deleter(static_cast<T*>(ptr));
        }
    };
    
    std::unique_ptr<Concept> concept_;
    
public:
    template<typename T, typename Deleter>
    AnyDeleter(Deleter deleter) 
        : concept_(std::make_unique<Model<T, Deleter>>(std::move(deleter))) {}
    
    void operator()(void* ptr) {
        concept_->destroy(ptr);
    }
};

// 使用示例
auto file_deleter = FILE* f { fclose(f); };
std::unique_ptr<FILE, AnyDeleter> file_ptr(fopen("test.txt", "r"), 
                                           AnyDeleter<FILE>(file_deleter));

7. 最佳实践总结

选择指南

cpp 复制代码
// 1. 独占所有权 -> unique_ptr
std::unique_ptr<Resource> resource = acquire_resource();

// 2. 共享所有权 -> shared_ptr
auto shared_resource = std::make_shared<SharedResource>();

// 3. 观察资源 -> weak_ptr
std::weak_ptr<SharedResource> observer = shared_resource;

// 4. 需要数组 -> unique_ptr<T[]> 或 shared_ptr<T> + 自定义删除器
std::unique_ptr<int[]> array = std::make_unique<int[]>(100);

错误用法警示

cpp 复制代码
// ❌ 错误:从原始指针创建多个shared_ptr
int* raw = new int(42);
std::shared_ptr<int> p1(raw);
std::shared_ptr<int> p2(raw);  // 未定义行为,双重释放

// ❌ 错误:循环引用
struct A { std::shared_ptr<B> b; };
struct B { std::shared_ptr<A> a; };

// ❌ 错误:返回unique_ptr的引用
std::unique_ptr<int>& get_ptr();  // 可能意外释放资源

// ✅ 正确做法
// 使用make_shared/make_unique
// 使用weak_ptr打破循环引用
// 按值返回unique_ptr(支持移动语义)

智能指针是现代C++内存管理的核心工具,深入理解其原理和最佳实践对于编写安全高效的C++代码至关重要。

相关推荐
人道领域1 天前
【零基础学java】(Stream流)
java·开发语言
两个蝴蝶飞1 天前
Java量化系列(九):实现股票列表自动同步,精准监控新增、更名与退市动态
java·开发语言
@zulnger1 天前
python 学习笔记(对象的方法)
笔记·python·学习
独自破碎E1 天前
Java对象是怎么在虚拟机中存储的?
java·开发语言
坚持学习前端日记1 天前
Android JS桥技术深度解析
android·开发语言·javascript
兮动人1 天前
打破 OS 壁垒:Java 跨平台硬件信息采集的“终极方案”
java·开发语言
咸鱼Doyoung1 天前
《commander-cpp》单头文件的、链式调用的、自动生成帮助文档的C++命令行参数解析库
c++
一路往蓝-Anbo1 天前
STM32单线串口通讯实战(一):物理层拓扑与STM32G0硬件配置
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网
weixin_307779131 天前
MATLAB动态演示流体扩散仿真模拟的简单例子
开发语言·matlab