有没有办法使用模板专门化来分隔new和new []?

时间:2010-04-07 08:19:43

标签: c++ templates specialization

我有一个自动指针类,在构造函数中我传入一个指针。我希望能够在构造函数中将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 个答案:

答案 0 :(得分:7)

不幸的是,没有。两者都返回相同的类型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);

答案 1 :(得分:3)

C ++ 0x中的

std::unique_ptr将具有动态数组的特化,如下所示。但是,实例化适当的实例将是用户的任务。在语言层面,没有办法区分一个指针与另一个指针。

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]);
}

此外,使用如此多样化的任务加载一个类可能不太好。例如,提升有shared_ptrshared_array

答案 2 :(得分:2)

另一方面,您可以使用特定的make函数。

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

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

当然,这意味着你有适当的逻辑,但它是封装的。您还可以添加重载,使用T复制传递给新创建的指针的对象等...

最后,它也可以通过构造函数的重载完成......重点是不要在外面调用new

答案 3 :(得分:2)

我认为真正的解决方案是摆脱你自己的autopointer类并摆脱使用C风格的数组。我知道之前已经说了很多次,但是使用C风格的数组真的没什么意义了。您可以使用std::vectorboost::array来完成您可以使用它们执行的所有操作。这两个都创建了不同的类型,因此可以重载它们。

答案 4 :(得分:2)

由于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() );

你可以让你的课程更复杂,这样就可以推断出删除的类型。

答案 5 :(得分:1)

new[]被明确定义为具有指针值,尽管无论如何都会引发数组到指针的隐式转换。

但我不认为你运气不好。毕竟,您的示例不是管理指向int的指针,它正在管理指向int[10]的指针。所以理想的方式是

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

正如Red-Nosed Unicorn所提到的,new int[10]不会创建C风格的数组。如果您的编译器也符合C标准,但C ++允许C风格的数组不仅仅是C 中的C风格数组。无论如何,如果你这样问,new会创建一个C风格的数组:

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

不幸的是,即使使用delete contents;int (*contents)[10];也无效。允许编译器做正确的事情:标准没有指定数组像new一样转换为指针,我相信我记得GCC替换delete[]并发出警告。但这是未定义的行为。

因此,您需要两个析构函数,一个用于调用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)

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

答案 6 :(得分:1)

第二次尝试......

很容易使智能指针类智能化数组。如您所知,如果您知道它是一个开头的数组,则不需要构造函数的运行时标志或参数。唯一的问题是newnew[]具有相同的返回类型,因此它们无法将此信息传递给智能指针类。

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[]的含义,因为访问者在C ++ 0x中提到std::unique_ptr

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] );