8

を介してオブジェクト B を所有するオブジェクト A があるとしますstd::unique_ptr<B>。さらに、B は A への生のポインタ (弱い) 参照を保持します。次に、A のデストラクタは、B を所有しているため、B のデストラクタを呼び出します。

B のデストラクタで A にアクセスする安全な方法は何でしょうか? (Aのデストラクタにもいる可能性があるため)。

A のデストラクタで B への強い参照を明示的にリセットして、B が予測可能な方法で破棄されるようにする安全な方法ですが、一般的なベスト プラクティスは何ですか?

4

5 に答える 5

3

B のデストラクタで A にアクセスする安全な方法は何でしょうか? (Aのデストラクタにもいる可能性があるため)。

安全な方法はありません:

3.8/1

[...] T 型のオブジェクトの有効期間は、次の場合に終了します。

— T が非自明なデストラクタ (12.4) を持つクラス型の場合、デストラクタ呼び出しが開始されます [...]

オブジェクトの有効期間が終了すると、オブジェクトにアクセスできないのは簡単だと思います。

編集: Chris Drew がコメントで書いたように、デストラクタが開始された後にオブジェクトを使用できます。申し訳ありませんが、標準の重要な文を 1 つ見逃していました。

3.8/5

オブジェクトの有効期間が開始する前であるが、オブジェクトが占有するストレージが割り当てられた後、またはオブジェクトの有効期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、ストレージを参照するポインターオブジェクトが配置される、または配置された場所を使用できますが、限られた方法でのみ使用できます。建設中または破壊中のオブジェクトについては、12.7 を参照してください。それ以外の場合、そのようなポインターは割り当てられたストレージ (3.7.4.2) を参照し、ポインターが void* 型であるかのようにポインターを使用することは明確に定義されています。このようなポインターは参照解除できますが、結果の左辺値は、以下で説明するように、限られた方法でのみ使用できます。次の場合、プログラムは未定義の動作をします: [...]

12.7 には、構築および破棄中にできることのリストがあり、最も重要なもののいくつかは次のとおりです。

12.7/3:

クラス X のオブジェクトを参照するポインタ(glvalue) を、 Xの直接的または間接的な基底クラス Bへのポインタ (参照) に明示的または暗黙的に変換するには、 X の構築と、その直接的または間接的な基底すべての構築B から直接的または間接的に派生するクラスが開始され、これらのクラスの破棄が完了していない必要があります。そうでない場合、変換は未定義の動作を引き起こします。オブジェクト obj の直接の非静的メンバーへのポインターを形成する (またはその値にアクセスする) には、 objの構築が開始され、その破棄が完了していない必要があります。そうしないと、ポインター値の計算 (またはメンバー値へのアクセス) が未定義の動作になります。

12.7/4

仮想関数 (10.3) を含むメンバー関数は、構築または破棄中に呼び出すことができます(12.6.2)。クラスの非静的データ メンバーの構築中または破棄中を含め、コンストラクタまたはデストラクタから仮想関数が直接的または間接的に呼び出され、呼び出しが適用されるオブジェクトが構築中のオブジェクト (x と呼ぶ) である場合または破壊の場合、呼び出される関数は、コンストラクタまたはデストラクタのクラスの最終オーバーライドであり、より派生したクラスでそれをオーバーライドするものではありません。仮想関数呼び出しが明示的なクラス メンバー アクセス (5.2.5) を使用し、オブジェクト式が x またはその基本クラス サブオブジェクトの 1 つではなく、x またはその基本クラス サブオブジェクトの 1 つの完全なオブジェクトを参照する場合、動作は未定義です。 .

于 2016-06-22T07:12:38.700 に答える
0

すでに述べたように、「安全な方法」はありません。実際、PcAF によって指摘されているように、のデストラクタAに到達するまでに、 の寿命はすでに終了していますB
また、これは実際には良いことであることも指摘しておきたいと思います! オブジェクトが破棄される厳密な順序が必要です。
今あなたがすべきことは、それが破壊されようとしていることB を事前に伝えることです. それは次のように簡単ですA

void ~A( void ) {
    b->detach_from_me_i_am_about_to_get_destructed( this );
}

this設計オブジェクトによっては、ポインターを渡す必要がある場合とそうでないB場合があります (B多くの参照を保持している場合は、どれをデタッチするかを知る必要がある場合があります。1 つしか保持していない場合、thisポインターは不要です)。
インターフェイスが意図した方法でのみ使用できるように、適切なメンバー関数がプライベートであることを確認してください。

注意A: これは、との間の通信を自分で完全に制御する場合に適した単純で軽量なソリューションですB。いかなる場合でも、これをネットワーク プロトコルとして設計しないでください。それには、より多くの安全柵が必要になります。

于 2016-06-22T08:21:36.653 に答える
0

このことを考慮:

struct b
{
        b()
        {
                cout << "b()" << endl;
        }
        ~b()
        {
                cout << "~b()" << endl;
        }
};

struct a
{
        b ob;
        a()
        {
                cout << "a()" << endl;
        }
        ~a()
        {
                cout << "~a()" << endl;
        }
};

int main()
{
        a  oa;
}

//Output:
b()
a()
~a()
~b()

「その後、A のデストラクタは B のデストラクタを呼び出します。B がそれを所有しているためです。」これは、複合オブジェクトの場合にデストラクタを呼び出す正しい方法ではありません。上記の例を見ると、最初aに破棄され、次にb破棄されます。aのデストラクタは のデストラクタを呼び出さないbため、コントロールは のデストラクタに戻りますa

「B のデストラクタで A にアクセスする安全な方法は何ですか?」. 上記の例のように、aは既に破棄されているためのデストラクタaではアクセスできませんb

「Aのデストラクタにもいる可能性があるため)。」. これは正しくありません。繰り返しますが、コントロールが のデストラクタから出ると、コントロールaだけが のデストラクタに入りbます。

デストラクタは、クラス T のメンバ関数です。制御がデストラクタの外に出ると、クラス T にアクセスできなくなります。クラス T のすべてのデータ メンバーは、上記の例のように、クラス T のコンストラクターとデストラクタでアクセスできます。

于 2016-06-22T08:26:26.623 に答える