You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现C++自定义shared_ptr时如何区分数组指针与普通指针

由于new T和new T[N]返回的指针类型完全一致,编译期无法直接通过类型区分指针来源,所以需要从调用入口做限制。以下两种解决方案都可以在C11及以上版本实现,不需要C17支持:

方案1:工厂函数隔离(最稳妥,推荐)

完全禁止用户直接传递裸指针构造智能指针,通过工厂函数从内部保证指针类型和删除器匹配:

  1. 将所有myptr接受裸指针的构造函数设为私有,仅允许工厂函数访问
  2. 分别实现非数组和数组版本的工厂函数:
// 非数组版本工厂函数
template <typename T, typename... Args>
myptr<T> make_myptr(Args&&... args) {
    return myptr<T>(new T(std::forward<Args>(args)...));
}

// 数组版本工厂函数
template <typename T>
myptr<T[]> make_myptr_array(size_t n) {
    return myptr<T[]>(new T[n]);
}
  1. 使用时只能通过工厂函数构造,完全避免传错:
// 正确用法
auto arr_ptr = make_myptr_array<char>(10); // 内部自动调用new char[10],匹配delete[]删除器
auto obj_ptr = make_myptr<char>('a'); // 内部自动调用new char,匹配delete删除器

// 错误用法根本无法写出,从调用层面杜绝未定义行为
// 即使写make_myptr_array<char>(1)也没问题,内部是new char[1],用delete[]删除完全合法

方案2:标签分派(保留裸指针构造能力)

如果业务需要保留用户直接传裸指针的能力,可以通过标签强制用户显式标注指针类型,避免误传:

  1. 定义空标签类型用于标记数组指针:
struct array_pointer_tag {};
constexpr array_pointer_tag array_ptr_tag{};
  1. 修改数组特化版本的构造函数,删除无标签的裸指针构造接口,仅保留带标签的构造函数:
template <typename T, typename DeleterType>
class myptr<T[], DeleterType> {
private:
    my_ptr_cnt<T, DeleterType> *p;
public:
    // 删除无标签的裸指针构造接口,避免误调用
    myptr(T* p) = delete;

    // 仅允许带标签的构造,附加的SFINAE约束和C++17标准库逻辑一致,避免派生类数组转基类数组的未定义行为
    template <typename Y, typename = typename std::enable_if<std::is_convertible<Y(*)[], T(*)[]>::value>::type>
    myptr(Y* p, array_pointer_tag, DeleterType deleter=[](T* p) {delete[] p;}) :
        p(new my_ptr_cnt<T, DeleterType>(static_cast<T*>(p), deleter)) { }
};
  1. 使用时必须显式传入标签,用户自然会确认传入的是数组指针:
// 正确用法
myptr<char[]> arr_ptr(new char[10], array_ptr_tag);

内容的提问来源于stack exchange,提问作者陈泽霖

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 02:00:05