在C++标准库的智能指针体系中,shared_ptr通过引用计数实现多个指针共享同一个堆对象的管理,其拷贝和移动操作的行为和普通原生指针、自定义对象都有明显差异,正确理解这些规则是合理使用shared_ptr的基础。

shared_ptr拷贝操作规则
当执行shared_ptr的拷贝构造或者拷贝赋值时,新生成的shared_ptr会和原shared_ptr共享被管理的对象,同时被管理对象的引用计数会增加1。拷贝操作不会重新分配被管理的对象内存,只是新增了一个指向该对象的智能指针实例。
拷贝构造示例
下面通过代码展示shared_ptr拷贝构造的行为:
#include <iostream>
#include <memory>
int main() {
// 创建shared_ptr,管理一个int对象,此时引用计数为1
std::shared_ptr<int> ptr1(new int(10));
std::cout << "ptr1引用计数: " << ptr1.use_count() << std::endl;
// 拷贝构造ptr2,ptr1和ptr2共享对象,引用计数加1
std::shared_ptr<int> ptr2(ptr1);
std::cout << "拷贝后ptr1引用计数: " << ptr1.use_count() << std::endl;
std::cout << "拷贝后ptr2引用计数: " << ptr2.use_count() << std::endl;
// 拷贝赋值,ptr3也会共享对象,引用计数再加1
std::shared_ptr<int> ptr3;
ptr3 = ptr2;
std::cout << "拷贝赋值后引用计数: " << ptr1.use_count() << std::endl;
// 三个指针指向的对象值相同
std::cout << "*ptr1: " << *ptr1 << ", *ptr2: " << *ptr2 << ", *ptr3: " << *ptr3 << std::endl;
return 0;
}
上述代码执行后,初始ptr1的引用计数为1,拷贝构造ptr2后引用计数变为2,拷贝赋值ptr3后引用计数变为3,三个指针解引用得到的值都是10,说明它们指向同一个对象。
拷贝操作的注意事项
- 只要还有任意一个shared_ptr持有对象的所有权,对象就不会被释放,只有当所有shared_ptr都被销毁或者重置时,引用计数降为0,被管理对象才会被删除。
- 拷贝操作是浅拷贝,不会复制被管理的对象本身,仅复制智能指针的控制块引用。
- 不要将同一个原生指针多次用来初始化不同的shared_ptr,这会导致引用计数错误,引发重复释放的问题。
shared_ptr移动操作规则
shared_ptr的移动构造和移动赋值属于资源所有权的转移操作,移动后原shared_ptr会变为空指针,不再持有被管理对象的所有权,被管理对象的引用计数不会增加。
移动构造示例
通过代码展示移动操作的行为:
#include <iostream>
#include <memory>
int main() {
// 创建shared_ptr,引用计数为1
std::shared_ptr<int> ptr1(new int(20));
std::cout << "移动前ptr1引用计数: " << ptr1.use_count() << std::endl;
std::cout << "移动前ptr1是否为空: " << (ptr1 ? "否" : "是") << std::endl;
// 移动构造ptr2,所有权从ptr1转移到ptr2
std::shared_ptr<int> ptr2(std::move(ptr1));
std::cout << "移动后ptr1引用计数: " << ptr1.use_count() << std::endl;
std::cout << "移动后ptr1是否为空: " << (ptr1 ? "否" : "是") << std::endl;
std::cout << "移动后ptr2引用计数: " << ptr2.use_count() << std::endl;
// 移动赋值
std::shared_ptr<int> ptr3;
ptr3 = std::move(ptr2);
std::cout << "移动赋值后ptr2是否为空: " << (ptr2 ? "否" : "是") << std::endl;
std::cout << "移动赋值后ptr3引用计数: " << ptr3.use_count() << std::endl;
return 0;
}
执行上述代码可以看到,移动操作后原shared_ptr变为空,引用计数始终为1,因为移动只是转移所有权,没有新增持有者。
移动操作的适用场景
- 当需要将shared_ptr作为返回值从函数中返回时,编译器通常会自动触发移动操作,避免不必要的引用计数增减,提升性能。
- 当明确不再需要使用原shared_ptr时,可以使用移动操作转移所有权,减少引用计数的维护开销。
- 移动操作不会改变被管理对象的生命周期,只是改变所有权的持有者。
拷贝与移动操作的核心差异对比
为了更清晰区分两种操作,我们将核心差异整理为如下表格:
| 对比项 | 拷贝操作 | 移动操作 |
|---|---|---|
| 引用计数变化 | 增加1 | 不变 |
| 原shared_ptr状态 | 仍然持有对象所有权 | 变为空指针,失去所有权 |
| 对象共享情况 | 多个shared_ptr共享同一对象 | 仅转移后的shared_ptr持有对象 |
| 性能开销 | 需要修改引用计数,有一定开销 | 仅转移控制块指针,开销更低 |
常见使用误区
误区1:认为移动shared_ptr会复制被管理的对象,实际上移动只转移所有权,对象本身不会被复制。
误区2:对shared_ptr执行拷贝后,修改其中一个指针指向的对象,其他指针指向的对象也会被修改,因为共享的是同一个对象。
在实际开发中,需要根据是否需要共享对象所有权来选择使用拷贝还是移动操作,合理运用这两种操作可以让内存管理更安全高效。
shared_ptr拷贝构造移动构造引用计数智能指针修改时间:2026-06-23 18:57:37