web-dev-qa-db-ja.com

メンバーはアドレスを取得するために初期化する必要がありますか?

メンバーを初期化する前に、データメンバーへのポインターを初期化できますか?つまり、これは有効なC++ですか?

#include <string>

class Klass {
public:
    Klass()
        : ptr_str{&str}
        , str{}
    {}
private:
    std::string *ptr_str;
    std::string str;
};

this 質問は私のものに似ていますが、順序は正しいので、答えは

誰かがクラスのメンバーの順序を変更した場合に備えて、このようにコーディングしないことをお勧めします。

これは順序を逆にすることは違法になることを意味するように思われますが、私は確信が持てませんでした。

23
Ayxan

メンバーはアドレスを取得するために初期化する必要がありますか?

番号。

メンバーを初期化する前に、データメンバーへのポインターを初期化できますか?つまり、これは有効なC++ですか?

はい。はい。

単項のオペランドと初期化する必要があるという制限はありません。単項&演算子の仕様の標準に例があります。

int a;
int* p1 = &a;

ここで、aの値は初期化されておらず、それをポイントしても問題ありません。

この例が示していないのは、オブジェクトの存続期間が始まる前にオブジェクトを指していることです。これは、この例で起こっていることです。 singストレージが占有されている場合、存続期間の前後のオブジェクトへのポインタが明示的に許可されます。標準ドラフトは言う:

[basic.life]オブジェクトのライフタイムが開始する前、ただしオブジェクトが占有するストレージが割り当てられた後、またはオブジェクトのライフタイムが終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、すべてのポインタオブジェクトが配置される、または配置された保管場所のアドレスを表すものを使用できますが、その使用方法は限定されています.

ルールは、使用がどのように制限されるかをリストし続けます。あなたは常識でうまくいくことができます。つまり、void*を扱うのと同じように扱うことができます。ただし、これらの制限に違反している場合は、形式が正しくなく、UBです。参照についても同様のルールがあります。

非静的メンバーのアドレスを具体的に計算することにも制限があります。標準ドラフトは言う:

[class.cdtor] ...オブジェクトの直接の非静的メンバーobjへのポインターを形成する(またはその値にアクセスする)には、objの構築が開始され、その破棄が完了していない場合、それ以外の場合、ポインター値の計算(またはメンバー値へのアクセス)は、未定義の動作になります。

Klassのコンストラクターでは、Klassの構築が開始され、破棄が完了していないため、上記のルールは満たされています。


追伸クラスはコピー可能ですが、コピーには別のインスタンスのメンバーへのポインターがあります。それがあなたのクラスにとって理にかなっているかどうかを検討してください。そうでない場合は、カスタムのコピーおよび移動コンストラクターと代入演算子を実装する必要があります。このような自己参照は、カスタムデストラクタではなくカスタム定義が必要になる場合があるまれなケースであるため、5(または3)のルールの例外です。

P.P.Sメンバーのいずれかを指し、メンバー以外のオブジェクトを指し示さない場合は、オブジェクトへのポインターではなく、メンバーへのポインターを使用することをお勧めします。

25
eerorika