2016-09-07 14 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]) {} 
}; 

コードは、それがbuff(new Type[n])が危険であると言うコースのPDFファイルからです。私はそれがなぜ安全でないのか分からない、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)の両方に警告が表示されます。 〜ウォール(または、より具体的には、~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'の後に実行される'noexcept'以外のコードがあっても、新しいクラスは失敗し、そこでは構築が失敗します。クラス内の他のものは動的に割り当てられないので、それ自体の後でクリーンアップされます。 – 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は定義されていません。

コンストラクタの初期化リストを使用することが推奨されますが、順序についての前提は作成しないように注意してください。

一般に、生ポインタを所有しないことをお勧めします。代わりにスマートポインタを使用する場合、データを解放することを忘れることはできません。

特定のケースでは、Cスタイルの配列の代わりにstd :: vectorを使用することもできます。これはスレッドの安全な方法ですべての割り当て、再割り当て、リリースなどを処理します。あなた自身の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に1人の以上のメンバーを追加する必要があることを想像して:

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のコンストラクタがスローした場合、デストラクタ~Array()が呼び出されることはありませんので、buffに割り当てられたメモリは、リークが発生します。あなたは(アカウントに例外を取って)自分でストレージの割り当てを解除する責任がある、と自動的にストレージ割り当て解除の世話をすることができ、そのようなstd::unique_ptrstd::shared_ptrなどのツールを紹介しますので、このようなType* buff生のポインタとしてポインタを呼び出す++

近代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_]) {} 
}; 

お知らせデストラクタの有無:あなたはこのようなあなたのクラスを書くことができ++現代Cで

unique_ptrdeleteを呼び出します。

も注意初期化子リスト内のクラスのメンバーに依存関係のない(単にnew Type[n_]の代わりnew Type[n]を書くことは、あなたのコードはより堅牢になり)

0

C++ 98標準12.6.2.5.4(私は新しいバージョンを期待していませんこれをリラックスさせるために)。

- 次に、非静的データメンバは、それらが が(再びかかわらず、MEM-初期化子の順序 の)クラス定義で宣言された順序で初期化されなければなりません。

したがって、初期化の順序はこれに従って定義されます。

クラッシュする方法の例が必要な場合は、sizeof(Type)*n>システムの総メモリを単純に作成してください。

関連する問題