C++ - 简单实现std::shared_ptr
我们手动实现一个简化版的shared_ptr,主要实现引用计数功能。注意,这只是一个教学示例,实际标准库的实现要复杂得多,考虑了线程安全、自定义删除器、类型转换等很多特性。
我们要实现的核心功能:
-
构造函数:从原始指针构造
-
拷贝构造函数:增加引用计数
-
赋值操作符:减少左值的引用计数,并增加右值的引用计数
-
析构函数:减少引用计数,当计数为0时销毁对象
-
解引用操作:operator* 和 operator->
-
获取引用计数:use_count()
我们要实现以下核心功能:
-
引用计数管理
-
拷贝构造和赋值
-
解引用操作
-
资源自动释放
MySharedPtr.h头文件:
#include <iostream>
#include <utility> // for std::swap
template<typename T>
class MySharedPtr
{
private:
T* m_ptr; // 托管对象的原始指针
int* m_refCount; // 引用计数指针
public:
// 1. 默认构造函数
MySharedPtr() : m_ptr(nullptr), m_refCount(nullptr) {}
// 2. 从原始指针构造
explicit MySharedPtr(T* ptr) : m_ptr(ptr), m_refCount(new int(1)) {
std::cout << "构造函数: 引用计数 = 1" << std::endl;
}
// 3. 拷贝构造函数 - 核心!
MySharedPtr(const MySharedPtr& other)
: m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
if (m_refCount) {
++(*m_refCount);
std::cout << "拷贝构造: 引用计数 = " << *m_refCount << std::endl;
}
}
// 4. 拷贝赋值运算符 - 最复杂!
MySharedPtr& operator=(const MySharedPtr& other) {
// 处理自赋值情况:ptr1 = ptr1;
if (this == &other) {
return *this;
}
// 先减少当前对象的引用计数
cleanup();
// 复制数据和引用计数
m_ptr = other.m_ptr;
m_refCount = other.m_refCount;
// 增加引用计数
if (m_refCount) {
++(*m_refCount);
std::cout << "拷贝赋值: 引用计数 = " << *m_refCount << std::endl;
}
return *this;
}
// 5. 移动构造函数 (C++11)
MySharedPtr(MySharedPtr&& other) noexcept
: m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
// 转移所有权后,将原对象置为空
other.m_ptr = nullptr;
other.m_refCount = nullptr;
std::cout << "移动构造: 转移所有权" << std::endl;
}
// 6. 移动赋值运算符 (C++11)
MySharedPtr& operator=(MySharedPtr&& other) noexcept {
if (this == &other) {
return *this;
}
cleanup();
m_ptr = other.m_ptr;
m_refCount = other.m_refCount;
other.m_ptr = nullptr;
other.m_refCount = nullptr;
std::cout << "移动赋值: 转移所有权" << std::endl;
return *this;
}
// 7. 析构函数
~MySharedPtr() {
cleanup();
}
// 8. 解引用操作符
T& operator*() const {
return *m_ptr;
}
// 9. 箭头操作符
T* operator->() const {
return m_ptr;
}
// 10. 获取原始指针
T* get() const {
return m_ptr;
}
// 11. 获取引用计数
int use_count() const {
return m_refCount ? *m_refCount : 0;
}
// 12. 判断是否唯一所有者
bool unique() const {
return use_count() == 1;
}
// 13. 重置功能
void reset(T* ptr = nullptr) {
cleanup();
if (ptr) {
m_ptr = ptr;
m_refCount = new int(1);
}
}
private:
// 清理资源的核心函数
void cleanup() {
if (m_refCount) {
--(*m_refCount);
std::cout << "清理资源: 引用计数 = " << *m_refCount << std::endl;
if (*m_refCount == 0) {
std::cout << "删除托管对象" << std::endl;
delete m_ptr;
delete m_refCount;
}
}
m_ptr = nullptr;
m_refCount = nullptr;
}
};
核心原理详解
1. 引用计数机制
// 关键数据结构
T* m_ptr; // 指向托管对象
int* m_refCount; // 指向引用计数器
// 所有拷贝都共享这两个指针
MySharedPtr(const MySharedPtr& other)
: m_ptr(other.m_ptr), m_refCount(other.m_refCount) {
++(*m_refCount); // 引用计数+1
}
2. 资源清理逻辑
void cleanup() {
if (m_refCount) {
--(*m_refCount); // 计数-1
if (*m_refCount == 0) { // 如果没人使用了
delete m_ptr; // 删除实际对象
delete m_refCount; // 删除计数器
}
}
}
3. 拷贝赋值的精妙处理
MySharedPtr& operator=(const MySharedPtr& other) {
if (this == &other) return *this; // 自赋值检查
cleanup(); // 先清理当前资源
// ... 然后复制新资源
}
测试代码
main.cpp
#include <iostream>
#include "MySharedPtr.h"
// 测试类
class TestObject
{
public:
TestObject(std::string name,int val = 0) : m_name(name),m_value(val)
{
std::cout << "TestObject 构造: " <<"m_name = " << m_name << " m_value = " << m_value << std::endl;
}
~TestObject()
{
std::cout << "TestObject 析构: " << "m_name = " << m_name << " m_value = " << m_value << std::endl;
}
void print() const
{
std::cout << "TestObject print函数: " << "m_name = " << m_name << " m_value = " << m_value << std::endl;
}
private:
std::string m_name;
int m_value;
};
int main()
{
std::cout << "=== 测试开始 ===" << std::endl;
// 测试1: 基本构造
{
TestObject* obj_1 = new TestObject("测试类", 100);
MySharedPtr<TestObject> ptr0{ obj_1 };
ptr0->print();
std::cout << "ptr0 计数: " << ptr0.use_count() << std::endl;
MySharedPtr<TestObject> ptr1{ ptr0 };
std::cout << "ptr0 计数: " << ptr0.use_count() << std::endl;
std::cout << "ptr1 计数: " << ptr1.use_count() << std::endl;
}
// 测试1: 基本构造和析构
{
std::cout << "\n--- 测试1: 基本生命周期 ---" << std::endl;
MySharedPtr<TestObject> ptr1(new TestObject("张三",100));
ptr1->print();
}
// 测试2: 拷贝构造
{
std::cout << "\n--- 测试2: 拷贝构造 ---" << std::endl;
MySharedPtr<TestObject> ptr1(new TestObject("李四", 200));
MySharedPtr<TestObject> ptr2 = ptr1; // 拷贝构造
std::cout << "ptr1 计数: " << ptr1.use_count() << std::endl;
std::cout << "ptr2 计数: " << ptr2.use_count() << std::endl;
}
// 测试3: 拷贝赋值
{
std::cout << "\n--- 测试3: 拷贝赋值 ---" << std::endl;
MySharedPtr<TestObject> ptr1(new TestObject("王五",300));
MySharedPtr<TestObject> ptr2(new TestObject("赵六",400));
ptr2 = ptr1; // 拷贝赋值
std::cout << "赋值后计数: " << ptr1.use_count() << std::endl;
}
// 测试4: 移动语义
{
std::cout << "\n--- 测试4: 移动语义 ---" << std::endl;
MySharedPtr<TestObject> ptr1(new TestObject("秦始皇",500));
MySharedPtr<TestObject> ptr2 = std::move(ptr1); // 移动构造
std::cout << "移动后ptr1计数: " << ptr1.use_count() << std::endl;
std::cout << "移动后ptr2计数: " << ptr2.use_count() << std::endl;
}
std::cout << "\n=== 测试结束 ===" << std::endl;
return 0;
}
输出结果:

与标准库的差异
我们的简化版本缺少了标准库的以下特性:
-
线程安全 - 标准库使用原子操作保证线程安全
-
自定义删除器 - 支持不同的资源释放策略
-
弱指针支持 - 与
weak_ptr的交互 -
类型转换 -
dynamic_pointer_cast等 -
异常安全 - 更完善的异常处理
-
数组特化 -
shared_ptr<T[]>
通过这个实现,你深入理解了:
-
引用计数原理 - 多个智能指针如何共享所有权
-
RAII思想 - 资源获取即初始化
-
拷贝控制 - 拷贝构造/赋值的正确处理
-
自赋值安全 -
ptr = ptr情况的处理 -
移动语义 - C++11的现代特性应用

浙公网安备 33010602011771号