2016-09-07 28 views
10

這可能是一個簡單的問題,但我有這個template class不安全的模板數組構造

template<typename Type> 
class Array { 
    size_t n; 
    Type* buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

的代碼是從課件pdf文件的地方說:buff(new Type[n])是不安全的。我不明白爲什麼它不安全,是不是size_t一般沒有簽名?我可以舉一個例子,說明它可能有編譯和/或運行時錯誤嗎?

+1

對於初學者來說,你沒有析構函數,這可能會導致內存泄漏 –

+0

@ArnavBorborah,但這是唯一的*不安全*的東西? – Loay

回答

12

該代碼是「不安全的」,因爲它依賴於在buff之前構建的n。這種依賴性增加了代碼的脆弱性。

當你構建他們在在類中聲明的順序構成的類的成員,而不是如何他們被稱爲在成員初始化列表,因此,如果代碼更改爲

template<typename Type> 
class Array { 
    Type* buff; 
    size_t n; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

那麼當你做buff(new Type[n])n是未初始化的,你有未定義的行爲。

+1

請注意,如果您將成員放入成員初始值設定項列表中的順序與構建的順序不同,[gcc和clang](http://coliru.stacked-crooked.com/a/08d42bdb5e31d8ed)會發出警告,而您已經啓動了-Wall(或者更具體的是-Wreorder)。 – jaggedSpire

+0

@ jaggedSpire,這是最讓我惱火的警告! – SergeyA

+0

@jaggedSpire這很好,我不知道它做到了。不好的人太多了,不要使用它。 – NathanOliver

-1

在初始化列表中調用new運算符並不安全。 如果new失敗,Array的析構函數將不會被調用。 這裏是一個類似的問題。 Are there any issues with allocating memory within constructor initialization lists?

+0

這不適用於這裏,但是如果在'new'之後執行一些非'nnxcept'代碼,甚至是另一個'new',如果new失敗,那麼構造會在那裏失敗,這個類中的其他任何東西都不會動態分配,所以它會自行清理。 – jaggedSpire

3

首先秩序的,對執行不被它們都寫下來的順序確定的構造函數初始化,而是由初始化字段出現在代碼的順序:

class Array { 
    size_t n; 
    Type* buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

這裏首先n將被初始化,然後buff。

class Array { 
    Type* buff; 
    size_t n; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
}; 

現在第一個buff會被初始化,然後n,所以n在這種情況下沒有定義的值。

使用初始化列表的構造函數是很好的做法,但要小心,不要創建順序的任何假設。

一般來說是從擁有原始指針不要一個好主意。如果您使用智能指針,則不能忘記釋放數據。

在特定情況下,你可能也想使用std :: vector的,而不是C風格的數組。它以一種線程安全的方式處理所有的分配,重新分配,釋放等操作。看起來你正在試圖寫一些類似你自己的std :: vector的東西。請僅爲教育目的這樣做。始終更喜歡生產代碼中的標準實施。相當長一段時間你可能不會變得更好。如果你沒有,你會問不同的問題在這裏;-)

+0

此示例代碼用於教育目的 – Loay

3

首先,你有內存泄漏。但問題可能不在於此。所以讓我們假設你有一個析構函數來釋放數組。

template<typename Type> 
class Array { 
    size_t n; 
    Type* buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n]) {} 
    ~Array() { delete[] buff; } 
}; 

現在這個特定的代碼是完全安全的。分配n_時不會拋出異常,初始化順序正確,buff是您班級中唯一的原始指針。但是,隨着您開始擴展課程和編寫更多課程,內存泄漏的風險會增加。

試想一下,你需要更多的一個成員添加到class Array

template<typename Type> 
class Array { 
    size_t n; 
    Type* buff; 
    SomethingElse xyz; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n_]), xyz(n_) {} 
    ~Array() { delete[] buff; } 
}; 

如果SomethingElse的構造函數拋出,分配給buff內存就會泄漏,因爲析構函數~Array()將永遠不會被調用。

現代C++調用指針如Type* buff原始指針因爲你是負責釋放存儲自己的(考慮例外情況考慮在內),並介紹了工具如std::unique_ptrstd::shared_ptr,能自動照顧存儲釋放的。

在現代C++中你可以寫你的類是這樣的:

template<typename Type> 
class Array { 
    size_t n; 
    std::unique_ptr<Type[]> buff; 
public: 
    Array(size_t n_): n(n_), buff(new Type[n_]) {} 
}; 

通知沒有析構函數。 unique_ptr將負責爲您撥打delete

還要注意在初始化列表裏面類成員不存在依賴關係(簡單地寫new Type[n_]而不是new Type[n]使你的代碼更健壯)

0

C++ 98標準12.6.2.5.4(我不希望新版本放鬆了這一點)。

- 然後,非靜態數據成員應在他們 在類定義中聲明(再次不分先後順序的MEM-初始化的 )的順序進行初始化。

所以初始化的順序是根據這個定義的。

如果您想要一個如何使其崩潰的示例,只需在系統中創建sizeof(Type)*n>全部內存。