문제

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]);
도움이 되었습니까?

해결책

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.

다른 팁

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

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.

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.

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.

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.

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] );
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top