4

共用体は、任意の時点でメンバーのリストから 1 つのオブジェクトのみを含む、ユーザー定義のデータ型またはクラス型です。考えられるすべての候補メンバーを動的に割り当てる必要があるとします。例えば。

// Union Destructor
#include <string>
using namespace std;

union Person
{
private:
    char* szName;
    char* szJobTitle;
public:
    Person() : szName (nullptr), szJobTitle (nullptr) {}
    Person (const string& strName, const string& strJob)
    {
        szName = new char[strName.size()];
        strcpy (szName, strName.c_str());

        szJobTitle = new char [strJob.size()];
        strcpy (szJobTitle, strJob.c_str());    // obvious, both fields points at same location i.e. szJobTitle
    }
    ~Person()   // Visual Studio 2010 shows that both szName and szJobTitle
    {           // points to same location.
        if (szName) {
            delete[] szName;     // Program crashes here.
            szName = nullptr;  // to avoid deleting already deleted location(!)
        }
        if (szJobTitle)
            delete[] szJobTitle;
    }
};

int main()
{
    Person you ("your_name", "your_jobTitle");
    return 0;
}

上記のプログラムは、~Person の最初の削除ステートメントでクラッシュします (szName に有効なメモリ位置が含まれているとき、なぜですか?)。

デストラクタの正しい実装は何ですか?

同じように、私のクラスにユニオンメンバーが含まれている場合、クラスオブジェクトを破棄する方法 ( Union を含むクラスのデストラクタを記述する方法)?

4

4 に答える 4

3

共用体は同じメモリーを共有しているため、一度に使用できる共用体のメンバーは 1 つだけです。ただし、コンストラクターでは、両方のメンバーを初期化して相互に上書きし、デストラクタでそれを 2 回解放することになります。構造体であるかのように使用しようとしています (構造体を使用する必要があるフィールドの名前に基づいて)。

それにもかかわらず、ユニオンが必要な場合は、おそらく、使用されているメンバーを表す ID を持つ一種のエンベロープとしての構造体と、リソースを処理するコンストラクターとデストラクタが必要です。

また、配列が小さすぎます。size()は文字数を返しますが、文字列型として使用する場合は、終了を処理するchar*ためにヌル文字 ( ) 用のスペースが必要です。\0

ユニオンが必要な場合は、Boost.Variant を使用してみてください。通常のユニオンよりもはるかに使いやすいです。

于 2013-04-18T10:20:15.753 に答える
2

ではなくを使用しているため、delete使用すべきときに を使用しています。delete []new []new

これらを変更します。

delete szName;
delete szJobTitle;

これらに:

delete [] szName;
delete [] szJobTitle;

ちなみに、ifデストラクタ内の条件は無意味です。つまり、ポインターが の場合、nullptrを安全に記述できますdelete ptr;。つまり、

A *ptr = nullptr;
delete ptr; //Okay! No need to ensure ptr is non-null

それとは別に、3 (または C++11 では 5) の規則に違反しています。

それらを実装します。

于 2013-04-18T09:54:34.280 に答える
1

上記のプログラムは、~Person の最初の削除ステートメントでクラッシュします (szName に有効なメモリ位置が含まれているとき、なぜですか?)。

私はコンパイラを持っていません(またはコードをコンパイルする時間はありません)が(Nawaz によって対処された問題は別として)、ユニオンメンバーをクラスメンバーとして扱っているためだと思います。ユニオンでは、szName と szJobTitle は、同じアドレスを持つ 2 つの変数のように見える必要があります。

Person (const string& strName, const string& strJob)
{
    szName = new char[strName.size()];
    strcpy (szName, strName.c_str());

    szJobTitle = new char [strJob.size()]; // this creates memory leak (1)
    strcpy (szJobTitle, strJob.c_str());
}

新しいメモリを割り当てて szJobTitle に配置するため、メモリ リークが発生します。&szJobTitle は &szName と同じメモリ ロケーションを使用するため、(1) 行の割り当てにより、szName で割り当てられたアドレスが失われます。szName と szJobTitle のタイプが異なる (メモリ フットプリントが一致しない) 場合、szJobTitle の設定も破損します (または szTitle を部分的に上書きするだけです)。

デストラクタの正しい実装は何ですか?

デストラクタを実装するための十分な詳細がないと思います。C++の判別共用体の概念を調べて、これを正しく実装する方法を確認してください。通常、ユニオンメンバーは独自のメモリを管理する必要があり (char* ではなく std::string を使用)、デストラクタは割り当てられたもののみを削除します (ただし、明示的に呼び出す必要があります)。

同じように、クラスにユニオンメンバーが含まれている場合、クラスオブジェクトを破棄する方法 (ユニオンを含むクラスのデストラクタを記述する方法)?

再び、差別された組合を見てください。これは基本的に共用体と列挙型の関連付けであり、列挙型は共用体のメンバーにマップされ、共用体のどのメンバーが設定されたかを指定するように設定されます。

于 2013-04-18T10:23:59.090 に答える