智能指针是 C++ 标准库(<memory> 头文件)提供的内存管理工具,核心目标是替代裸指针(T*),自动管理堆内存的生命周期,避免手动 new/delete 导致的内存泄漏、重复释放、野指针等问题。其底层原理是RAII(资源获取即初始化):将堆内存的释放逻辑绑定到智能指针的析构函数,当智能指针出作用域/被销毁时,自动调用 delete 释放指向的内存。

一、智能指针的核心概念

1. 所有权模型

智能指针的核心是对“堆对象所有权”的管理,主要分为两类:

  • 独占所有权:一个对象只能被一个智能指针管理(如 unique_ptr);
  • 共享所有权:多个智能指针可以共享一个对象,最后一个指针销毁时才释放对象(如 shared_ptr);
  • 弱引用:不拥有对象所有权,仅观察共享对象(如 weak_ptr)。

2. 核心优势

  • 自动释放内存:无需手动调用 delete,避免遗忘释放导致的内存泄漏;
  • 异常安全:即使代码中途抛出异常,智能指针析构函数仍会执行,保证内存释放;
  • 避免重复释放:通过所有权规则,杜绝多次 delete 同一内存;
  • 野指针防护:智能指针销毁后自动置空(部分场景),减少野指针风险。

二、C++ 智能指针的全量用法

C++ 标准库提供 3 种核心智能指针(C++11 及以上),以及 C++14 新增的辅助创建函数,以下是完整用法:

1. std::unique_ptr(独占式智能指针)

核心特性
  • 独占对象所有权,不可拷贝,仅可移动(std::move);
  • 轻量级(仅封装一个裸指针),无额外性能开销;
  • 支持管理数组(unique_ptr<T[]>)。
常用操作
操作 说明
std::make_unique<T>(args...) C++14 新增:创建 unique_ptr,自动调用 T 的构造函数(推荐);
unique_ptr<T> ptr(new T()) 手动创建(不推荐,异常安全差);
ptr->func() / (*ptr).func() 访问对象成员(同裸指针);
ptr.reset() 释放对象,指针置空;
ptr.reset(new T()) 释放旧对象,指向新对象(不推荐,优先用 make_unique);
ptr.release() 释放所有权,返回裸指针(需手动管理内存);
ptr.get() 获取内部裸指针(仅观察,不释放所有权);
std::move(ptr) 转移所有权,原指针变为空;
unique_ptr<T[]> arr_ptr 管理数组(自动调用 delete[],而非 delete);
示例代码
#include <memory>
#include <iostream>

class Computer {
public:
    Computer(const std::string& brand) : brand_(brand) {
        std::cout << brand_ << " 电脑创建" << std::endl;
    }
    ~Computer() {
        std::cout << brand_ << " 电脑销毁" << std::endl;
    }
    void powerOn() {
        std::cout << brand_ << " 开机" << std::endl;
    }
private:
    std::string brand_;
};

// unique_ptr 基础用法
void unique_ptr_demo() {
    // 1. 创建:优先用 make_unique(C++14)
    std::unique_ptr<Computer> comp1 = std::make_unique<Computer>("联想");
    
    // 2. 访问成员
    comp1->powerOn();
    
    // 3. 移动所有权(不可拷贝)
    std::unique_ptr<Computer> comp2 = std::move(comp1); // comp1 变为空
    if (!comp1) {
        std::cout << "comp1 已失去所有权" << std::endl;
    }
    comp2->powerOn();
    
    // 4. 管理数组
    std::unique_ptr<Computer[]> comp_arr = std::make_unique<Computer[]>(2);
    comp_arr[0] = Computer("戴尔");
    comp_arr[1] = Computer("惠普");
    
    // 5. 释放所有权(需手动delete)
    Computer* raw_ptr = comp2.release();
    delete raw_ptr; // 必须手动释放,否则内存泄漏
}

2. std::shared_ptr(共享式智能指针)

核心特性
  • 共享对象所有权,可拷贝、可赋值;
  • 内部维护引用计数:每拷贝一次,计数+1;每销毁一个指针,计数-1;计数为 0 时释放对象;
  • 支持自定义删除器(如管理非 new 创建的资源);
  • 注意:避免循环引用(否则计数无法归 0,导致内存泄漏)。
常用操作
操作 说明
std::make_shared<T>(args...) 创建 shared_ptr,内存更高效(一次分配对象+引用计数);
shared_ptr<T> ptr(new T()) 手动创建(不推荐,两次内存分配);
ptr.use_count() 获取当前引用计数;
ptr.unique() 判断是否独占对象(计数为 1);
ptr.reset() 释放当前指针的所有权,计数-1;
ptr.get() 获取内部裸指针;
std::static_pointer_cast 智能指针版的 static_cast
std::dynamic_pointer_cast 智能指针版的 dynamic_cast
示例代码
void shared_ptr_demo() {
    // 1. 创建
    std::shared_ptr<Computer> comp1 = std::make_shared<Computer>("苹果");
    std::cout << "引用计数:" << comp1.use_count() << std::endl; // 输出 1
    
    // 2. 拷贝:计数+1
    std::shared_ptr<Computer> comp2 = comp1;
    std::cout << "引用计数:" << comp1.use_count() << std::endl; // 输出 2
    
    // 3. 重置:计数-1
    comp1.reset();
    std::cout << "引用计数:" << comp2.use_count() << std::endl; // 输出 1
    
    // 4. 自定义删除器(比如管理 FILE* 资源)
    auto file_deleter = [](FILE* fp) {
        fclose(fp);
        std::cout << "文件已关闭" << std::endl;
    };
    std::shared_ptr<FILE> fp_ptr(fopen("test.txt", "w"), file_deleter);
}

3. std::weak_ptr(弱引用智能指针)

核心特性
  • 不拥有对象所有权,仅观察 shared_ptr 管理的对象;
  • 不影响引用计数,避免循环引用;
  • 需转换为 shared_ptr 才能访问对象(通过 lock() 方法)。
常用操作
操作 说明
weak_ptr<T> wp = sp shared_ptr 构造弱引用;
wp.lock() 转换为 shared_ptr:对象存在则返回有效指针,否则返回空;
wp.expired() 判断观察的对象是否已被释放;
wp.use_count() 获取观察对象的引用计数;
示例代码(解决循环引用)
// 循环引用场景:两个对象互相持有 shared_ptr,导致计数无法归 0
class A;
class B;

class A {
public:
    std::weak_ptr<B> b_ptr; // 用 weak_ptr 替代 shared_ptr
    ~A() { std::cout << "A 销毁" << std::endl; }
};

class B {
public:
    std::weak_ptr<A> a_ptr; // 用 weak_ptr 替代 shared_ptr
    ~B() { std::cout << "B 销毁" << std::endl; }
};

void weak_ptr_demo() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    
    // 互相引用:weak_ptr 不增加计数
    a->b_ptr = b;
    b->a_ptr = a;
    
    // 访问弱引用对象:需 lock() 转换为 shared_ptr
    if (auto b_sp = a->b_ptr.lock()) {
        std::cout << "B 对象有效" << std::endl;
    }
}

4. 废弃的智能指针(不推荐使用)

  • std::auto_ptr:C++98 引入,已被 unique_ptr 替代(拷贝语义有缺陷,会转移所有权,易出错);
  • 注意:不要使用 auto_ptr,优先用 unique_ptr

三、综合示例:智能指针实战场景

以下示例模拟“电脑设备管理系统”,综合使用 unique_ptr(独占设备)、shared_ptr(共享配置)、weak_ptr(观察状态),覆盖核心用法:

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

// 配置类:多台电脑共享同一配置
class Config {
public:
    Config(int memory, std::string os) : memory_(memory), os_(os) {
        std::cout << "配置创建:内存 " << memory_ << "G,系统 " << os_ << std::endl;
    }
    ~Config() {
        std::cout << "配置销毁" << std::endl;
    }
    void show() const {
        std::cout << "当前配置:" << memory_ << "G | " << os_ << std::endl;
    }
private:
    int memory_;
    std::string os_;
};

// 电脑类:独占自身硬件,共享配置
class Computer {
public:
    Computer(std::string brand, std::shared_ptr<Config> cfg) 
        : brand_(brand), cfg_(cfg), status_(std::make_unique<bool>(false)) {}
    
    ~Computer() {
        std::cout << brand_ << " 销毁" << std::endl;
    }
    
    void powerOn() {
        *status_ = true;
        std::cout << brand_ << " 开机:";
        cfg_->show();
    }
    
    // 提供弱引用观察状态(不拥有所有权)
    std::weak_ptr<bool> getStatus() const {
        return std::shared_ptr<bool>(status_); // 转换为 shared_ptr 后构造 weak_ptr
    }

private:
    std::string brand_;
    std::shared_ptr<Config> cfg_; // 共享配置
    std::unique_ptr<bool> status_; // 独占状态(开机/关机)
};

int main() {
    // 1. 创建共享配置(多台电脑共用)
    std::shared_ptr<Config> global_cfg = std::make_shared<Config>(16, "Windows 11");
    
    // 2. 创建多台电脑(独占自身,共享配置)
    std::vector<std::unique_ptr<Computer>> computers;
    computers.emplace_back(std::make_unique<Computer>("联想", global_cfg));
    computers.emplace_back(std::make_unique<Computer>("戴尔", global_cfg));
    computers.emplace_back(std::make_unique<Computer>("惠普", global_cfg));
    
    // 3. 开机并观察状态
    for (auto& comp : computers) {
        comp->powerOn();
        
        // 弱引用观察状态
        std::weak_ptr<bool> status_wp = comp->getStatus();
        if (auto status_sp = status_wp.lock()) {
            std::cout << "状态:" << (*status_sp ? "开机" : "关机") << std::endl;
        }
    }
    
    // 4. 查看配置引用计数
    std::cout << "配置引用计数:" << global_cfg.use_count() << std::endl; // 输出 4(3台电脑 + global_cfg)
    
    // 5. 销毁部分电脑
    computers.erase(computers.begin() + 1); // 删除戴尔电脑
    std::cout << "删除戴尔后,配置计数:" << global_cfg.use_count() << std::endl; // 输出 3
    
    return 0;
}

输出结果

配置创建:内存 16G,系统 Windows 11
联想 开机:当前配置:16G | Windows 11
状态:开机
戴尔 开机:当前配置:16G | Windows 11
状态:开机
惠普 开机:当前配置:16G | Windows 11
状态:开机
配置引用计数:4
戴尔 销毁
删除戴尔后,配置计数:3
惠普 销毁
联想 销毁
配置销毁

示例说明

  1. shared_ptr<Config>:3 台电脑共享同一配置,只有最后一个指针销毁时,配置才会释放;
  2. unique_ptr<Computer>:vector 中存储独占式指针,删除元素时自动释放对应电脑对象;
  3. weak_ptr<bool>:观察电脑状态,不影响状态对象的生命周期,避免循环引用;
  4. 全程无手动 delete,所有内存由智能指针自动释放,无内存泄漏。

四、关键注意事项

  1. 头文件:所有智能指针都需要包含 <memory> 头文件;
  2. 版本兼容make_unique 是 C++14 特性,C++11 需手动写 unique_ptr<T>(new T())
  3. 避免裸指针混用:不要将智能指针的裸指针(get() 返回)再封装为智能指针,否则会重复释放;
  4. 循环引用shared_ptr 互相引用时,必须用 weak_ptr 打破循环;
  5. 数组管理unique_ptr<T[]> 专门管理数组,自动调用 delete[],而 shared_ptr 需自定义删除器;
  6. 性能unique_ptr 无额外开销,shared_ptr 因引用计数有轻微开销(原子操作)。

总结

  1. 智能指针的核心是RAII 机制,通过析构函数自动释放内存,替代裸指针的手动管理;
  2. unique_ptr 适用于独占所有权场景(轻量、高效),shared_ptr 适用于共享所有权场景(需注意循环引用),weak_ptr 用于观察共享对象
  3. 优先使用 make_unique/make_shared 创建智能指针,避免手动 new,提升异常安全性;
  4. 智能指针是 C++ 内存管理的最佳实践,彻底解决裸指针的内存泄漏、重复释放等问题。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐