代码之家  ›  专栏  ›  技术社区  ›  Marlon

有没有一种方法可以使用模板专门化将new与new[]分开?

  •  14
  • Marlon  · 技术社区  · 15 年前

    我有一个自动指针类,在构造函数中我传入一个指针。我希望能够在构造函数中分离new和new[],以便在析构函数中正确调用delete或delete[]。这可以通过模板专门化完成吗?我不想在构造函数中传入布尔值。

        template <typename T>
        class MyAutoPtr
        {
        public:
          MyAutoPtr(T* aPtr);
        };
    
    // in use:
    MyAutoPtr<int> ptr(new int);
    MyAutoPtr<int> ptr2(new int[10]);
    
    7 回复  |  直到 15 年前
        1
  •  3
  •   visitor    15 年前

    std::unique_ptr 在C++中,0x将具有动态数组的专门化,如下所示。但是,实例化适当的实例将是用户的任务。在语言级别,无法区分一个指针和另一个指针。

    template <class T>
    class pointer
    {
        T* p;
    public:
        pointer(T* ptr = 0): p(ptr) {}
        ~pointer() { delete p; }
        //... rest of pointer interface
    };
    
    template <class T>
    class pointer<T[]>
    {
        T* p;
    public:
        pointer(T* ptr = 0): p(ptr) {}
        ~pointer() { delete [] p; }
        //... rest of pointer and array interface
    };
    
    int main()
    {
        pointer<int> single(new int);
        pointer<int[]> array(new int[10]);
    }
    

    此外,在一个类中加载如此多的任务可能没有那么好。例如,Boost shared_ptr shared_array .

        2
  •  7
  •   Konrad Rudolph    15 年前

    不幸的是,没有。两者都返回相同的类型, T* . 考虑使用调用适当重载构造函数的生成器函数:

    template <typename T>
    class MyAutoPtr
    {
    public:
        MyAutoPtr(T* aPtr, bool array = false);
    };
    
    template <typename T>
    MyAutoPtr<T> make_ptr() {
        return MyAutoPtr<T>(new T(), false);
    }
    
    template <typename T>
    MyAutoPtr<T> make_ptr(size_t size) {
        return MyAutoPtr<T>(new T[size], true);
    }
    

    现在可以按如下方式实例化对象:

    MyAutoPtr<int> ptr = make_ptr<int>();
    MyAutoPtr<int> ptr2 = make_ptr<int>(10);
    
        3
  •  2
  •   Matthieu M.    15 年前

    另一方面,您可以使用特定的 make 功能。

    template <class T>
    MyAutoPtr<T> make();
    
    template <class T>
    MyAutoPtr<T> make(size_t n);
    

    当然,这意味着您有适当的逻辑,但它是封装的。您还可以添加过载 T 要复制传递到新创建的指针等中的对象…

    最后,还可以通过构造函数的重载来完成。关键是不要说 new 在外面。

        4
  •  2
  •   anon    15 年前

    我认为真正的解决方案是去掉你自己的autopointer类,并去掉使用C样式数组。我知道这已经说过很多次了,但是使用C样式的数组已经没有什么意义了。几乎所有你能用它们做的事情都可以用 std::vector 或与 boost::array . 这两种类型都会创建不同的类型,所以 可以 他们负担过重。

        5
  •  2
  •   Kirill V. Lyadvinsky    15 年前

    这是不可能的,因为 new int[X] 生成指向数组初始元素的指针。它的类型和 int*

    常见的解决方案之一是 删除程序 . 向类中再添加一个模板参数,以便为指针传递自定义的删除器。这会使你的班更普及。您可以创建如下的默认删除程序:

    struct default_deleter
    {
        template<typename T>
        void operator()( T* aPtr ) { delete aPtr; }
    };
    

    对于数组,可以传递自定义删除器:

    struct array_deleter
    {
        template<typename T>
        void operator()( T* aPtr ) { delete[] aPtr; }
    };
    

    最简单的实现是:

    template <typename T, typename D>
    class MyAutoPtr
    {
    public: 
        MyAutoPtr(T* aPtr, D deleter = default_deleter() ) : ptr_(aPtr), deleter_(deleter) {};
        ~MyAutoPtr() { deleter_(ptr_); }
    protected:
        D deleter_;
        T* ptr_;
    };
    

    然后您可以使用它,如下所示:

    MyAutoPtr<int, array_deleter> ptr2(new int[10], array_deleter() );
    

    您可以使类更复杂,这样它就可以为deleter推断类型。

        6
  •  1
  •   Potatoswatter    15 年前

    new[] 被专门定义为具有指针值,尽管数组到指针的隐式转换无论如何都会启动。

    但我不认为你走运。毕竟,您的示例没有管理指向 int ,它正在管理指向 int[10] . 所以理想的方法是

    MyAutoPtr<int[10]> ptr2(new int[10]);
    

    红鼻子独角兽提到, new int[10] 不创建C样式数组。如果编译器也符合C标准,但是C++允许C样式数组多于C样式数组。 在C中 . 不管怎样, new 如果您这样问,将创建一个C样式数组:

    MyAutoPtr<int[10]> ptr2(new int [1] [10]);
    

    不幸的是, delete contents; 即使与 int (*contents)[10]; . 编译器被允许做正确的事情:标准没有指定数组被转换为指针 新的 我想我记得海湾合作委员会取代 delete[] 发出警告。但这是未定义的行为。

    所以,你需要两个析构函数,一个来调用 delete 还有一个电话 删除[] . 由于不能部分专门化函数,因此该功能需要部分专门化的助手

    template< class T > struct smartptr_dtor {
        void operator()( T *ptr ) { delete ptr; }
    };
    
    template< class T, size_t N > struct smartptr_dtor< T[N] > {
        void operator()( T (*ptr) [N] ) { delete [] ptr; }
    };
    
    template< class T >
    void proper_delete( T *p ) {
        smartptr_dtor< T >()( p );
    }
    

    因为某种原因,我只是屈服于;v)

    不幸的是,这不适用于动态大小的数组,所以我将写下另一个答案。

        7
  •  1
  •   Potatoswatter    15 年前

    第二次尝试

    让一个智能指针类智能化数组是很容易的。正如您所怀疑的,如果您知道构造函数是一个数组,那么就不需要向它提供运行时标志或参数。唯一的问题是 new new[] 具有相同的返回类型,因此它们无法将此信息传递给智能指针类。

    template< class T, bool is_array = false >
    struct smartptr {
        T *storage;
    
        smartptr( T *in_st ) : storage( in_st ) {}
    
        ~smartptr() {
            if ( is_array ) delete [] storage; // one of these
            else delete storage; // is dead code, optimized out
        }
    };
    
    smartptr< int > sp( new int );
    smartptr< int, true > sp2( new int[5] );
    

    替代 bool 标志是超载的意思 T[] 正如客人所说 std::unique_ptr 在C++0x中执行。

    template< class T >
    struct smartptr {
        T *storage;
    
        smartptr( T *in_st ) : storage( in_st ) {}
    
        ~smartptr() { delete storage; }
    };
    
    template< class T > // partial specialization
    struct smartptr< T [] > {
        T *storage; // "T[]" has nothing to do with storage or anything else
    
        smartptr( T *in_st ) : storage( in_st ) {}
    
        ~smartptr() { delete [] storage; }
    };
    
    smartptr< int > sp( new int );
    smartptr< int[] > sp2( new int[5] );