C++的智能指针是标准库提供的用于动态内存管理的模板类,能够自动释放所指向的堆内存,避免手动调用delete带来的内存泄漏、重复释放等问题。目前标准库主要提供了shared_ptr、unique_ptr、weak_ptr三种智能指针,各自有不同的适用场景和特性。

常用智能指针的使用方法
unique_ptr:独占所有权的智能指针
unique_ptr是独占型智能指针,同一时间只能有一个unique_ptr指向同一块堆内存,无法通过拷贝构造或拷贝赋值转移所有权,只能通过移动语义转移。
基本使用示例如下:
#include <memory>
#include <iostream>
class Test {
public:
Test() { std::cout << "Test构造" << std::endl; }
~Test() { std::cout << "Test析构" << std::endl; }
void print() { std::cout << "Test print" << std::endl; }
};
int main() {
// 初始化unique_ptr
std::unique_ptr<Test> ptr1(new Test());
// 调用指向对象的方法
ptr1->print();
// 转移所有权,转移后ptr1变为空
std::unique_ptr<Test> ptr2 = std::move(ptr1);
if (!ptr1) {
std::cout << "ptr1已为空" << std::endl;
}
// ptr2离开作用域时自动释放内存
return 0;
}
shared_ptr:共享所有权的智能指针
shared_ptr是共享型智能指针,多个shared_ptr可以指向同一块堆内存,内部通过引用计数记录指向该内存的智能指针数量,当引用计数为0时自动释放内存。
使用示例如下:
#include <memory>
#include <iostream>
class Test {
public:
Test() { std::cout << "Test构造" << std::endl; }
~Test() { std::cout << "Test析构" << std::endl; }
};
int main() {
// 初始化shared_ptr
std::shared_ptr<Test> ptr1(new Test());
{
// 拷贝构造,引用计数加1
std::shared_ptr<Test> ptr2 = ptr1;
std::cout << "当前引用计数:" << ptr1.use_count() << std::endl;
}
// ptr2离开作用域,引用计数减1
std::cout << "当前引用计数:" << ptr1.use_count() << std::endl;
return 0;
}
weak_ptr:弱引用智能指针
weak_ptr是弱引用智能指针,它指向shared_ptr管理的对象,但不会增加引用计数,主要用于解决shared_ptr的循环引用问题。
使用示例如下:
#include <memory>
#include <iostream>
class B;
class A {
public:
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A析构" << std::endl; }
};
class B {
public:
std::weak_ptr<A> a_ptr; // 使用weak_ptr避免循环引用
~B() { std::cout << "B析构" << std::endl; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;
// 离开作用域后A和B都能正常析构
return 0;
}
智能指针的底层实现原理
unique_ptr的实现逻辑
unique_ptr的核心是通过删除拷贝构造和拷贝赋值函数,只允许移动构造和移动赋值,同时内部封装了原始指针,在析构函数中调用delete释放内存。简化实现示例如下:
template <typename T>
class MyUniquePtr {
private:
T* ptr;
public:
// 构造函数
explicit MyUniquePtr(T* p = nullptr) : ptr(p) {}
// 禁止拷贝构造
MyUniquePtr(const MyUniquePtr&) = delete;
// 禁止拷贝赋值
MyUniquePtr& operator=(const MyUniquePtr&) = delete;
// 移动构造
MyUniquePtr(MyUniquePtr&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
// 移动赋值
MyUniquePtr& operator=(MyUniquePtr&& other) noexcept {
if (this != &other) {
delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
}
return *this;
}
// 析构函数
~MyUniquePtr() {
delete ptr;
}
// 重载箭头运算符
T* operator->() const { return ptr; }
// 重载解引用运算符
T& operator*() const { return *ptr; }
// 判断是否为空
operator bool() const { return ptr != nullptr; }
};
shared_ptr的实现逻辑
shared_ptr内部除了保存指向对象的原始指针,还会维护一个指向引用计数对象的指针,引用计数对象记录了当前指向该资源的shared_ptr数量和控制块信息。简化实现示例如下:
template <typename T>
class MySharedPtr {
private:
T* ptr;
int* count; // 引用计数
public:
// 构造函数
explicit MySharedPtr(T* p = nullptr) : ptr(p), count(nullptr) {
if (ptr) {
count = new int(1);
}
}
// 拷贝构造,引用计数加1
MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), count(other.count) {
if (count) {
(*count)++;
}
}
// 拷贝赋值
MySharedPtr& operator=(const MySharedPtr& other) {
if (this != &other) {
// 先处理当前对象的引用计数
if (count && --(*count) == 0) {
delete ptr;
delete count;
}
// 赋值新对象的信息
ptr = other.ptr;
count = other.count;
if (count) {
(*count)++;
}
}
return *this;
}
// 析构函数
~MySharedPtr() {
if (count && --(*count) == 0) {
delete ptr;
delete count;
}
}
// 获取引用计数
int use_count() const {
return count ? *count : 0;
}
// 重载箭头运算符
T* operator->() const { return ptr; }
// 重载解引用运算符
T& operator*() const { return *ptr; }
};
智能指针使用注意事项
- 不要将原始指针和智能指针混合使用,避免同一块内存被多次释放
- 不要用同一个原始指针初始化多个shared_ptr,会导致引用计数错误
- 优先使用make_shared和make_unique初始化智能指针,比直接new更安全,效率更高
- 避免shared_ptr的循环引用,出现循环引用场景时使用weak_ptr打破循环
- unique_ptr作为函数返回值不需要显式调用std::move,编译器会自动优化为移动构造
三种智能指针的对比
| 智能指针类型 | 所有权特性 | 引用计数 | 适用场景 |
|---|---|---|---|
| unique_ptr | 独占所有权 | 无 | 明确资源只会被一个所有者持有,不需要共享的场景 |
| shared_ptr | 共享所有权 | 有 | 资源需要被多个所有者共同使用的场景 |
| weak_ptr | 弱引用 | 不增加计数 | 解决shared_ptr循环引用,或者需要临时访问shared_ptr资源但不想影响生命周期的场景 |
C++智能指针shared_ptrunique_ptrweak_ptr修改时间:2026-07-04 07:27:36