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

7 回答 7

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);
于 2010-04-07T08:21:32.870 回答
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

于 2010-04-07T08:35:50.943 回答
2

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

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

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

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

最后,也可以通过构造函数的重载来完成……关键是不要调用new外部。

于 2010-04-07T08:27:29.767 回答
2

我认为真正的解决方案是摆脱您自己的自动指针类并摆脱对 C 样式数组的使用。我知道这已经说过很多很多次了,但是使用 C 风格的数组真的没有多大意义了。几乎所有你能用它们做的事情都可以用std::vector或来完成boost::array。并且这两者都创建了不同的类型,因此您可以重载它们。

于 2010-04-07T08:44:16.333 回答
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() );

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

于 2010-04-07T20:26:47.170 回答
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];. 允许编译器做正确的事情:标准没有指定将数组转换为与 with 一样的指针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)

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

于 2010-04-07T09:01:30.690 回答
1

第二次尝试……</p>

使智能指针类对数组具有智能非常容易。正如您所怀疑的,如果您知道它是一个数组开头,则不需要构造函数的运行时标志或参数。唯一的问题是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标志的替代方法是重载访问者在 C++0x 中T[]提到的含义。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] );
于 2010-04-07T20:13:32.400 回答