有没有办法使用模板专门化来将 new 与 new[] 分开?

发布于 2024-08-28 03:14:00 字数 341 浏览 5 评论 0原文

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

I have an auto pointer class and in the constructor I am passing in a pointer. I want to be able to separate new from new[] in the constructor so that I can properly call delete or delete[] in the destructor. Can this be done through template specialization? I don't want to have to pass in a boolean in the constructor.

    template <typename T>
    class MyAutoPtr
    {
    public:
      MyAutoPtr(T* aPtr);
    };

// in use:
MyAutoPtr<int> ptr(new int);
MyAutoPtr<int> ptr2(new int[10]);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

浅笑依然 2024-09-04 03:14:00

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

Unfortunately, no. Both return the same type, T*. Consider using builder functions that call an appropriate overloaded constructor:

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

Now you can instantiate objects as follows:

MyAutoPtr<int> ptr = make_ptr<int>();
MyAutoPtr<int> ptr2 = make_ptr<int>(10);
雾里花 2024-09-04 03:14:00

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

此外,在一个类中加载如此多的任务可能也不是那么好。例如,boost 有 shared_ptrshared_array

std::unique_ptr in C++0x will have a specialization for dynamic arrays, somewhat like shown below. However, it will be the user's task to instantiate an appropriate instance. At language level there is no way to distinguish one pointer from another.

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

Furthermore, it might not be that good to load one class with so various tasks. For example, boost has shared_ptr and shared_array.

情丝乱 2024-09-04 03:14:00

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

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

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

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

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

On the other hand, you could use a specific make function.

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

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

Of course, this means that you have the appropriate logic behind, but it's encapsulated. You can also add overload taking a T to copy the object passed into the pointer newly created etc...

Finally, it can also be done with overloads of the constructor... the point is not to call the new outside.

她比我温柔 2024-09-04 03:14:00

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

I think the real solution is to get rid of your own autopointer class and to get rid of the use of C-style arrays. I know this has been said many, many times before, but there really isn't much point in using C-style arrays any more. Just about everything you can do with them can be done using std::vector or with boost::array. And both of these create distinct types, so you can overload on them.

柏林苍穹下 2024-09-04 03:14:00

这是不可能的,因为 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() );

您可以使您的类更复杂,以便它可以推断出删除器的类型。

It is not possible since new int[X] yields a pointer to the initial element of the array. It has the same type as int*.

One of the common solutions is to use deleters. Add one more template argument to your class so you could pass custom deleter for your pointer. It'll make your class more universal. You could create default deleter like the following:

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

And for arrays you could pass custom deleter:

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

The simplest implementation will be:

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_;
};

Then you could use it as follows:

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

You could make your class more complex so it could deduce type for deleter.

錯遇了你 2024-09-04 03:14:00

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

不幸的是,deletecontents; 即使使用 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)

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

new[] is specifically defined to have pointer value despite the array-to-pointer implicit conversion that would kick in anyway.

But I don't think you're out of luck. After all, your example isn't managing a pointer to an int, it's managing a pointer to an int[10]. So the ideal way is

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

As Red-Nosed Unicorn mentions, new int[10] does not create a C-style array. It will if your compiler complies to the C standard as well, but C++ allows C-style arrays to be more than C-style arrays in C. Anyway, new will create you a C-style array if you ask like this:

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

Unfortunately, delete contents; will not work even with int (*contents)[10];. The compiler is allowed to do the right thing: the standard doesn't specify that the array is converted to a pointer as with new, and I believe I recall GCC substituting delete[] and emitting a warning. But it's undefined behavior.

So, you will need two destructors, one to call delete and one to call delete[]. Since you can't partially specialize a function, the functionality demands a partially specialized helper

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

which for some reason I just subjected myself to ;v)

Unfortunately, this doesn't work with dynamic-sized arrays, so I'm going to write up another answer.

终陌 2024-09-04 03:14:00

第二次尝试……

让智能指针类对数组变得智能是很容易的。正如您所怀疑的,如果您知道它是一个数组,则不需要构造函数的运行时标志或参数。唯一的问题是 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] );

Second attempt…

It's quite easy to make a smart pointer class smart about arrays. As you suspected, you don't need a runtime flag or argument to the constructor if you know it's an array to begin with. The only problem is that new and new[] have identical return types, so they cannot pass this information to the smart pointer class.

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

An alternative to the bool flag is to overload the meaning of T[] as Visitor mentions std::unique_ptr does in 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] );
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文