2010-04-07 82 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()); 

你可以讓你的課更復雜,因此它可以推斷出刪除者的類型。