2009-10-19 23 views
14

コンストラクタとデストラクタでstd :: vector型のクラスメンバのstd :: vector :: clear()が呼び出されるコードで、何回も出会うことがあります。コンストラクタとデストラクタのstd :: vector :: clear()

これは必須ですなぜ私は表示されません。

  1. コンストラクタ - 型のstdのクラスメンバー::ベクトルは、デフォルトでは空であるので、(明確に呼び出す必要はありません)。
  2. デストラクタ - タイプstd :: vectorのクラスメンバは、オブジェクトの標準破壊の一部として破棄されます。ベクトル破棄の一部として、すべて値オブジェクトが破棄されます(メモリへのヒープポインタが割り当てられている場合、それらは "手動で"削除する必要があります)ので、再度clear()を呼び出す必要はありません。

私は何かお見逃しですか?

答えて

16

物事の音から、そのコードを書いた人は何かを逃した人でした。 ctorまたはdtorでclear()を呼び出すことは、他のコードの真中にある唯一の時間です。たとえば、ctorは一部のデータを読み込んで処理し、次により多くのデータを読み込む可能性があります。このような場合、毎回新しいコンテナを作成するよりも、データを読み込んだときにそのデータを1つのコンテナで使用し、毎回消去するほうが速いでしょう。

5

いいえ、そうです。いくつかの追加コンストラクタのビジネス(または基底クラスのコンストラクタ)ことを必要とするが、チャンスは非常に低いです...

その後編集のデストラクタの場合

、ものがある場合を除き私が見た最も一般的な間違いは、明確なメソッドがポインタ(ベクトル)のベクトルの削除を呼び出すと仮定しているということです。

22

いいえ、あなたは何も欠けていません。私はこれを(無害な)ブードゥー・プログラミング、それを解放した後にヌルへのポインタを設定したり、GUIコードでrepaint/revalidateをランダムに呼び出すようなものだと考えています。プログラマーは過去に何らかのバグを助けていたことを記憶しており、今や不必要に「ちょうどその時」に追加しています。誰が知っている、多分それは助けるだろう。ブードゥー。

+2

nice term、 'voodoo programming' :) – xtofl

+7

nullへのポインタを設定することは、ブードーではなく、デバッグに大きく役立ちます。また、同じポインタを2回削除するのを防ぎますが、誰もこれを実行しません - そうですか? –

+12

何かがある場合は、同じポインタを2回削除したことを知りたいと思っています。偶発的に成功するよりも大声で失敗すること(そしてそれについて調べること)が偶然に成功するよりも良いです(あなたに噛み付くバグをマスキングする) –

7
  1. それはコンテナがポインタを含むない限りデストラクタでのSTLコンテナの内容をクリアするためにunnessecaryだコンストラクタ
  2. にSTLコンテナの内容を消去するには完全に unnessecaryです。ポインタがの新しいを使用して作成されている場合、それでもが削除されたが最初に必要です。その後、コンテナを掃除することはまだ必要ではありません。

はこのことを考えてみましょう:

#define BOOST_TEST_MODULE StlContainers 
#define BOOST_LIB_DIAGNOSTIC 

#include <boost/test/unit_test.hpp> 
#include <boost/assign.hpp> 
#include <boost/assign/list_of.hpp> 
#include <boost/assign/std/vector.hpp> 

#include <vector> 

using namespace boost::assign; 
using namespace std; 

const vector<int> my_ints_vector = list_of(0)(1)(1)(2)(3)(5)(8)(13)(21)(34); 

struct ScopedStruct1 
{ 
     ScopedStruct1(const vector<int> & v) : m_v(v) {} 
     ~ScopedStruct1() {} 
    private : 
     vector<int> m_v; 
}; 

class A 
{ 
    public : 
     A(int i) : m_i(i) {} 
     ~A() {} 
    private : 
     int m_i; 
}; 

struct ScopedStruct2 
{ 
    ScopedStruct2() {} 
    ~ScopedStruct2() { for(vector<A*>::iterator it = m_v.begin(); it != m_v.end(); ++it) delete *it; } 

    vector<A*> m_v; 
}; 

struct ScopedStruct3 
{ 
    ScopedStruct3() {} 
    ~ScopedStruct3() { /* no deletion */ } 

    vector<A*> m_v; 
}; 

BOOST_AUTO_TEST_CASE(StlContainer_storing_something_simple) 
{ 
    ScopedStruct1 str(my_ints_vector); 
} 

BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_with_delete) 
{ 
    ScopedStruct2 str; 
    for(int i = 0; i < 10; i++) 
     str.m_v.push_back(new A(i)); 
} 

BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_without_delete) 
{ 
    ScopedStruct3 str; 
    for(int i = 0; i < 10; i++) 
     str.m_v.push_back(new A(i)); 
} 

私は3のテストケースを作成しましたブーストのunit_testフレームワークを使用します。 unit_testフレームワークはメモリリークを追跡するためgreateです。 1番目と2番目のテストケースはメモリリークを生成しませんが、3番目のケースはベクタの内容が削除されないためです。。コースの一つで

+0

サンプルコードで唯一の答えです。nice! – nkint

-4

は(0)または同等の発言権(のstd :: _割り当てを解除する前に、デストラクタでDestroy_range(...)

割り当て解除はアロケータを介して行われる(クリア)コールまたはサイズを変更することがあります。 :任意のデストラクタを実行していないの割り当てを解除するそれはちょうどメモリを解放割り当てられたバッファに(0)最初のサイズにデストラクタを実行する()物事をリサイズする

同等ではありません)(

明らか。ちょうど割り当てられたポインタ、ファイルハンドル、ミューテックスを保持し、オブジェクトが保持する他のすべての回復可能なリソース。デストラクタを実行しなければならない(MUST)。インスタンス化の前に、テンプレートはデストラクタが簡単であることを知らない。デストラクタが簡単な場合は、インスタンシエーション後に最適化されます

+1

いいえ、デストラクタは、ベクターが有効範囲外になったときに必ず呼び出されます。デストラクタを呼び出さないことは意味がありません。実装を慎重に検討することをお勧めします。あなたはこのような何かを実行することによってこれをテストすることができます。 の#include の#include 構造体のテスト{〜テスト(){のstd :: coutの<< "さようなら、世界\ nを"; }}; int main(){std :: cout << "作成する\ n"; std :: vector t(1); std :: cout << "作成された\ n"; } – janm

+5

絶対に間違っています。 –

+0

私は彼が私の読み間違いのユーザ定義クラスのベクトルメンバインスタンス変数に対する明確な呼び出しの議論ではなく、〜vector()で明確な呼び出しを議論するように読んだ。 – pgast

1

ここでは、有用な場所はどこにあるのでしょうか?デストラクタは、デストラクタの順序がどこで、デストラクタがベクターのオブジェクトが確実に破壊されるか何かの前に。

もちろん、それを必要としないようにコードを構造化する方がいいです。しかし、それは考えられる理由です。

1

これまで説明した内容にもかかわらず、デストラクタでclearを明示的に呼び出す必要があるシナリオが少なくとも1つあります。

破壊されるオブジェクトがいくつかの特定の破壊順序を必要とする、すなわちサブオブジェクトが何らかの形で互いに依存し、望ましくない結果を招く状況を想像してください。ご存じのように、メンバのサブオブジェクトの破棄(およびメンバの初期化)の順序は、クラス定義におけるメンバの宣言の順序によって決まります。だから、適切な破壊の順序を達成する一つの方法は、それに応じてメンバーの宣言を整理することです。しかし、第一に、これはメンテナンス上非常に良い解決策ではありません。第2に、破壊の望ましい順序は、いくつかの実行時条件に依存することがある。第3に、破壊の望ましい順序は、初期化の所望の鉱石と矛盾するかもしれない。これはすべて、宣言を並べ替えることによって適切な破壊順序を命ずることができない(または賢明ではない)可能性があることを意味します。

この場合、妥当なアプローチは、破壊順序依存性が「消える」まで、cleanメソッドなどを呼び出して、いくつかのクリティカルメンバーサブオブジェクトを手動でクリーンアップすることです。あなたが見たコードは、戦略的に選択されたvectorサブオブジェクトのcleanを呼び出すことによって問題を注文することに解決しようとしていたと思います。

コンストラクタでcleanを呼び出すと...誰かがそういったことをする理由がわかりません。

+0

Apperentlyあなたはあまりにもリアリズム的な言葉に住んでいます:)私はコードを調べるたびに、コンストラクタ/デストラクタで "クリーン"を達成しようとしました。あなたが描写している状況に関して、私はそれを甘やかすことはほとんどありません。それを避けるために最善を尽くさなければならないし、可能でない場合はよくコメントしてください。 – dimba

+0

私は間違っているかもしれませんが、stlオブジェクトがスコープの終わりで死んでいるときなどです。オブジェクト内のオブジェクトのデストラクタを呼び出します。そのオブジェクトがstlコンテナでもある場合、自動的にそのオブジェクトもクリアされます。例外的な状況はポインタが "new"によって開始された場合に発生します - 通常通り – Maciek

+1

@Maciek:誰もそれを主張しません。私が言っていることは、自動*クリーンアップの順序が場合によっては受け入れられないということです。自動クリーンアップが開始される前に、カスタムオーダーの事前クリーンアップを実行する必要がある場合があります。 – AnT

関連する問題