C++实现自定义std::shared_ptr如何编译期选择适配数组的特殊删除器
问题原因
你遇到的报错核心是内存释放逻辑不匹配:
new T[10]分配的数组需要调用delete[]释放,会逐个析构数组内所有元素,再释放整块内存- 你当前的默认删除器是
std::default_delete<Type>,内部调用的是delete,只会析构第一个元素,且和new[]的内存分配逻辑不匹配,因此触发malloc释放错误
你之前写的std::conditional不生效的原因是判断条件错误:std::is_array_v<t_SharedPTR> 检查的是SharedPTR类本身是不是数组类型,结果永远为假,永远不会选到数组版本的删除器。
修正方案
和标准库std::shared_ptr的实现逻辑对齐,做两处核心修改:
1. 调整默认删除器的自动选择逻辑
将模板参数的默认删除器改为根据Type是否为数组类型自动选择,同时引入需要的头文件:
#include <type_traits> #include <cstddef> #include <functional> // 主模板:普通非数组类型 template<typename Type, class TDeleter = std::conditional_t< std::is_array_v<Type>, std::default_delete<Type>, std::default_delete<Type> >> class SharedPTR final { using t_SharedPTR = SharedPTR<Type, TDeleter>; using ptr_type = Type*; using deleter_type = TDeleter; // 删除原来成员变量处的std::conditional初始化逻辑,直接使用默认构造即可 deleter_type deleter; ptr_type data = nullptr; long* count = nullptr; // ... 其余原有成员逻辑保持不变 };
2. 增加数组类型的偏特化
专门处理SharedPTR<T[]>的数组使用场景,同时增加数组下标访问运算符:
// 数组类型偏特化 template<typename Type, class TDeleter> class SharedPTR<Type[], TDeleter> final { using t_SharedPTR = SharedPTR<Type[], TDeleter>; using ptr_type = Type*; using deleter_type = TDeleter; deleter_type deleter; ptr_type data = nullptr; long* count = nullptr; void increment_count(){ if(data != nullptr){ (*count)++; } }; public: SharedPTR() = default; SharedPTR(std::nullptr_t) : data(nullptr), count(nullptr){}; explicit SharedPTR(ptr_type ptr){ data = ptr; count = new long(0); increment_count(); }; // 拷贝、移动、赋值、析构逻辑和主模板完全一致 SharedPTR(const t_SharedPTR& other) : data(other.data), count(other.count){ increment_count(); }; SharedPTR(t_SharedPTR&& other) noexcept : data(other.data), count(other.count), deleter(other.deleter){ other.data = nullptr; other.count = nullptr; }; ~SharedPTR(){ release(); }; t_SharedPTR& operator=(t_SharedPTR&& other) noexcept{ release(); data = other.data; count = other.count; deleter = other.deleter; other.data = nullptr; other.count = nullptr; return *this; }; t_SharedPTR& operator=(Type* ptr){ release(); data = ptr; if(data != nullptr){ count = new long(0); increment_count(); } return *this; }; t_SharedPTR& operator=(const t_SharedPTR& other){ release(); data = other.data; count = other.count; deleter = other.deleter; increment_count(); return *this; }; // 新增数组下标访问 Type& operator[](size_t idx) const { return data[idx]; } // 其余成员函数(get、use_count、release、reset等)和主模板保持一致即可 Type* get() const { return data; }; operator bool() const{ return get() != nullptr; }; long use_count() const { return count ? *count : 0; } bool unique() const { return use_count() == 1; } void release(){ if(count != nullptr){ if(*count == 1){ deleter(data); delete count; } else { (*count)--; } }; }; void reset(ptr_type ptr = nullptr){ release(); data = ptr; count = new long(0); increment_count(); }; void swap(t_SharedPTR& sharedPTR){ std::swap(data, sharedPTR.data); std::swap(count, sharedPTR.count); }; };
使用方式
和标准库std::shared_ptr保持一致,存储数组时显式声明数组类型:
int main() { SharedPTR<T[]> a(new T[10]); // 显式指定T[]类型,自动匹配数组版本删除器 return 0; }
运行后会正确打印10次~T(),不会出现内存释放错误。
如果想要避免用户误将数组指针传入非数组类型的SharedPTR<T>,可以在主模板的构造函数中加入静态断言实现编译期报错:
explicit SharedPTR(ptr_type ptr){ static_assert(!std::is_array_v<Type>, "数组类型请使用SharedPTR<T[]>声明"); data = ptr; count = new long(0); increment_count(); };
内容的提问来源于stack exchange,提问作者Dilshod Nozimov
相关产品推荐
相关产品推荐

