web-dev-qa-db-ja.com

組合員の解体業者が呼ばれている

C++ 11はunionで標準のレイアウトタイプの使用を許可しました: 組合員はユーザー定義のコンストラクターを持っています

私の質問は次のとおりです。unionがスコープ外になったときに、カスタムデストラクタが呼び出されることは保証されますか?

私が理解していることは、切り替え時に手動で破棄して構築する必要があるということです http://en.cppreference.com/w/cpp/language/union#Explanation

しかし、このような例についてはどうでしょう:

{
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

sがスコープ外になったとき、stringのデストラクタを呼び出さなかったため、ヒープに割り当てられた文字列をメモリからリークしましたか?

24
Jonathan Mee

あなたが提供したあなたの例ではstrは破壊されません。 [class.union]/2の標準状態

共用体は、メンバー関数(コンストラクターとデストラクターを含む)を持つことができますが、仮想(10.3)関数を持つことはできません。労働組合は基本クラスを持たないものとします。共用体は基本クラスとして使用されません。共用体に参照型の非静的データメンバーが含まれている場合、プログラムの形式が正しくありません。ユニオンの最大1つの非静的データメンバーは、中かっこまたは等しい初期化子を持つことができます。 [注:ユニオンの非静的データメンバーに重要なデフォルトコンストラクター(12.1)、コピーコンストラクター(12.8)、移動コンストラクター(12.8)、コピー代入演算子(12.8)がある場合)、移動代入演算子(12.8)、またはデストラクタ(12.4)の場合、ユニオンの対応するメンバー関数はユーザーが指定する必要があります。そうしないと、ユニオンに対して暗黙的に削除されます(8.4.3)。 —終了ノート]

重点鉱山

したがって、strvecの両方に自明ではない特別なメンバー関数があるので、それらを自分でユニオンに提供する必要があります。

空のデストラクタの下にある bogdan's コメントに従って、十分ではないことに注意してください。 [class.union]/8には

[...] Xが共用体である場合、そのバリアントメンバーは非静的データメンバーです; [...]

したがって、この共用体のすべてのメンバーはバリアントです。次に、[class.dtor]/8を見ると、

デストラクタの本体を実行し、本体内に割り当てられた自動オブジェクトを破棄した後、クラスXのデストラクタは、Xの直接の非バリアント非静的データメンバーのデストラクタを呼び出します[...]

そのため、デストラクタはユニオンのメンバーをバリアントとして自動的に破壊しません。

タグ付きユニオンkennytm のように作成できます ここ

struct TU {
   int type;
   union {
     int i;
     float f;
     std::string s;
   } u;

   TU(const TU& tu) : type(tu.type) {
     switch (tu.type) {
       case TU_STRING: new(&u.s)(tu.u.s); break;
       case TU_INT:    u.i = tu.u.i;      break;
       case TU_FLOAT:  u.f = tu.u.f;      break;
     }
   }
   ~TU() {
     if (tu.type == TU_STRING)
       u.s.~string();
   }
   ...
};

これにより、正しいメンバーが破棄されるか、std::variantまたはboost::variant

17
NathanOliver

あなたの例はコンパイルされません。ユニオンには、デフォルトで、デストラクタが削除されています。もちろん、どのデストラクタを呼び出す必要がありますか?確かに両方を呼び出すことはできません。また、実際に構成されたメンバーについての情報はどこにも保存されていません。適切なデストラクタを提供するのはあなた次第です。

コードスニペットをコンパイルしようとしたときのGCCの出力は次のとおりです。

In function ‘int main()’:
error: use of deleted function ‘main()::<anonymous union>::~<constructor>()’
       vector<int> vec; } s = { "Hello, world"s };
                                                ^

note: ‘main()::<anonymous union>::~<constructor>()’ is implicitly deleted because the default definition would be ill-formed:
      union { string str;
            ^
4
G. Sliepen

構造体のオブジェクトのコンストラクターは、自明でない型で常に手動で呼び出す必要があります。

通常、常に明示的にそれらを構築する必要もあります。ここでの割り当てが機能するのは奇妙なようです。

疑問がある場合でも、デストラクタが呼び出された場合はいつでもアセンブリを確認できます。

このコードのアセンブリは、basic_stringコンストラクタを呼び出しますが、デストラクタは呼び出しません。したがって、ここでリークが発生します。

using namespace std;
int main(int argc, char** argv){
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

アセンブリを表示するリンク: https://godbolt.org/g/wKu3vf

1
Hayt