2


0

リソース管理の順序が重要な理由

Stroustrupの著書The C ++ Programming languageで、彼は「通常、リソースが取得の逆の順序でリリースされることが重要である」と述べています。

void acquire()
{
 acquire resource 1;
 ...
 acquire resource n;
 use resources;
 release resource n;
 ...
 release resource 1;
}

次のように順序を変更するとどうなりますか?

void acquire()
{
   acquire resource 1;
    ...
   acquire resource n;
   use resources;
   release resource 1;
    ...
   release resource n;
}

4 Answer


4


そうしないと、デッドロックやリークの状況に陥ることがあります。 Obj1を宣言し、Obj2-Obj3を割り当てるとします。 1を解放する前に、2と3を解放する必要があります。 ただし、実際には、コンストラクタとデストラクタは正しい順序で呼び出されるため、これは心配することではありません。


3


1つの理由は、通常、ネストされたメモリ割り当てがあることです。

E.g. you have a class
class A{
  FILE *fp;
  char *name;
  xxx;
}

最初に「新しいA」を使用してAのリソースを割り当て、後でAの「fp」と「name」を操作する必要があります。 したがって、リソースを解放するときは、最初に fp`と name`を解放し、次に `A`を解放する必要があります。


1


最も一般的な意味では、「重要な場合にのみ重要」です。依存関係によって、必要な破壊順序が決まります。

ここでの既存の回答は、ネストされたリソースをトポロジの逆順に解放することに関するものです。

しかし、C ++はオブジェクト内のリソースが初期化される順序も定義します。 つまり、メンバーの構築では、以前に初期化された「兄弟姉妹」を使用できます。つまり、依存関係があり、それらの兄弟は破棄時にも有効であることを意味します。

class A {
    A( std::string *link2str );
    ~A();
    std::string *important_state;
};

class B {
    string first; // initialization occurs in the order of declaration
    A second;
    B() : first(), second( &first ) { } // not the constructor's list order
};

この例が良いデザインに見えるわけではありませんが、あなたは決して知りません。 _時々_メンバー間に何らかの依存関係がある理由があります。


0


実際のコードでは、リソースは通常ネストされており、オブジェクトを構築するときに使用可能なものはすべて、破棄するときに使用できるようにしたいと考えています。 C ++には確定的な破壊があるため、何らかの順序を選択する必要があり、FILO(「先入れ先出し」)がうまく機能します。

ネストの最も簡単な例はクラスです。 それらは常に、最初にベースを構築し、次にメンバー、次に派生したctorのボディを構築することで常に構築されます。

struct Base {
  Base() { cout << "Base\n"; }
};
struct Member {
  Member() { cout << "Member\n"; }
};

struct Derived : Base {
  Member member;
  Derived() { cout << "Derived\n"; }
};

これで、この例に同様の出力dtorを追加できます。

Derivedが破壊される前にBaseを破壊することはできません(Derivedのdtorは他の問題の中でも特に使用する必要があるかもしれません)。メンバーも同じです。