2010-04-07 42 views
14

我有一个自动指针类,并在构造函数中传递一个指针。我希望能够在构造函数中分离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]); 
+0

无论如何,这是一个很好的问题:这真的让你希望在语言本身中有一个适当的'array'处理:( – 2010-04-07 08:33:27

+0

)你不应该使用布尔参数来区分类型,因为这个决定是在编译时做出的,所以你应该在类型中表示它,这意味着你为普通指针和数组创建一个智能指针类。 – 2010-04-07 08:43:49

回答

3

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_ptrshared_array

+0

其实我不明白为什么Boost有两个,因为'shared_ptr'在构造时使用了'Deleter'参数(如果不存在则默认),这样您就可以为数组情况设置一个参数。 – 2010-04-07 08:46:48

+0

你需要从指针指向数组的一件事是'operator []'。然后'shared_array p(new int [10])''可能是'shared_ptr p(new int [10],array_deleter ());'尽管它可能很好地使用'shared_ptr'和deleter在引擎盖下。通常我认为单个对象和数组之间有很大的区别:如果一个函数使用'shared_ptr ',它是否也会处理动态数组? – visitor 2010-04-07 08:57:44

+0

这种方式对我来说最有意义,我只是不喜欢制作2个单独的类的想法:D懒我 – Marlon 2010-04-07 09:12:11

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); 
+0

有一种方法可以在传递到神庙函数时区分指针和数组,不幸的是,就编译器而言,新的和新的[]匹配完全相同的返回类型 – 2010-04-07 08:35:23

+1

@Ramon:可以区分数组中的指针,但是'new []'**不会创建一个数组,而C++中的数组只是那些使用'T [N]'语法,'N'是一个编译时常量。 – 2010-04-07 09:00:37

+1

'new []'确实创建了一个数组。它只是不返回指向数组的指针,它返回一个指向它创建的数组的第一个元素的指针。 5.3.4/1:“如果[实体]是一个数组,* new-expression *返回一个指向数组初始元素的指针”。 – 2010-04-07 10:23:15

2

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

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

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

当然,这意味着你有适当的背后的逻辑,但它的封装。你也可以添加超负荷取T复制传递到新创建的指针等对象...

最后,它也可以用重载的构造函数来完成......重点不在于调用外部的new

2

我认为真正的解决方案是摆脱你自己的autopointer类,并摆脱使用C风格的数组。我知道这已经被很多次说过了,但是使用C风格的数组真的没有太多意义。几乎所有可以用它们做的事情都可以使用std::vectorboost::array来完成。这两种创建不同的类型,所以你可以过载。

1

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];工作。编译器被允许做正确的事情:标准没有指定数组被转换为指针,与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); 
} 
出于某种原因,我只是受到自己

; 5)

不幸的是,这并不dynamic-工作因此我将写出另一个答案。

+0

@Potatocorn:'new int [10]'does * not * create'int [10]'。尽管您编写的代码可以编译,但是没有意义。模板类型和构造函数值之间没有关联。 – 2010-04-07 09:40:44

+0

@Red:'new T [10]'不是指向T [10]'的指针的结果如何?返回类型是'T *',但不能反映内存中对象的类型。 – Potatoswatter 2010-04-07 09:56:30

+0

5.3.4/1:“如果实体是一个非数组对象,new-expression将返回一个指向创建对象的指针,如果它是一个数组,则new-expression返回一个指向数组初始元素的指针“。 – Potatoswatter 2010-04-07 10:01:07

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[]的含义与访客提到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]); 
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()); 

你可以让你的课更复杂,因此它可以推断出删除者的类型。