2

本番環境でのクラッシュのデバッグに苦労しました。セマンティクスについてここの人々に確認したかっただけです。次のようなクラスがあります...

class Test {
public:
  Test()
  {
    // members initialized ...
    m_str = m_str;
  }
  ~Test() {}
private:
  // other members ...
  std::string m_str;
};

誰かが初期化を変更して、コードセマンティクス内で合理的に正しいctor初期化リストを使用しました。初期化の順序とその初期値は、とりわけ正しいものです。したがって、クラスは次のようになります...

class Test {
public:
  Test() 
    : /*other inits ,,, */ m_str(m_str)
  {
  }
  ~Test() {}
private:
  // other members ...
  std::string m_str;
};

しかし、コードが突然クラッシュし始めました!このコードの一部に初期化の長いリストを分離しましたm_str(m_str)リンクテキストで確認しました。

クラッシュする必要がありますか?規格はこれについて何と言っていますか?(それは未定義の動作ですか?)

4

5 に答える 5

15

最初のコンストラクターは次と同等です

  Test()
  : m_str()
  {
    // members initialized ...
    m_str = m_str;
  }

つまり、コンストラクター内で割り当てに到達するまでに、m_str 既に空の文字列に暗黙的に初期化されています。したがって、self への代入は、完全に無意味で不必要ですが、問題を引き起こしません (なぜならstd::string::operator=()、よく書かれた代入演算子は自己代入をチェックし、この場合は何もしないからです)。

ただし、2 番目のコンストラクターではm_str、初期化子リスト内のそれ自体で初期化しようとしています - その時点ではまだ初期化されていません。したがって、結果は未定義の動作になります。

更新:プリミティブ型の場合、これはまだ未定義の動作です (ガベージ値を持つフィールドが生成されます) が、クラッシュしません (通常 - 例外については以下のコメントを参照してください)。プリミティブ型には定義上、コンストラクター、デストラクタがなく、ポインターが含まれていないためです。他のオブジェクトに。

所有権セマンティクスを持つポインター メンバーを含まない型についても同様です。std::stringこれにより、これらのいずれでもないことが示されます:-)

于 2010-10-08T15:50:40.947 に答える
2

割り当てによる元の「初期化」は完全に不要です。

割り当ての時点で m_str メンバーは既定で既に初期化されているため、プロセッサ サイクルを浪費する以外に害はありませんでした。

2 番目のコード スニペットでは、デフォルトの初期化がオーバーライドされ、まだ初期化されていないメンバーを使用して自身を初期化します。それは未定義の動作です。そして、それは完全に不必要です: それを削除するだけです (そして、元の時間の浪費を再導入しないでください。削除するだけです)。

コンパイラの警告レベルを上げることで、このコードや同様の単純に良くないコードに関する警告を受け取ることができる場合があります。

残念ながら、あなたが抱えている問題はこの技術的なものではなく、もっと根本的なものです。それは、自動車工場の労働者が、彼らが新しい自動車ブランドに付けている四角いホイールについて質問をするようなものです. 問題は、四角いホイールが機能しないことではなく、多くのエンジニアやマネージャーが、派手な外見の四角いホイールを使用するという決定に関与し、誰も反対しなかったことです.何人かは間違いなく反対しました.四角い車輪が機能しないことを理解していませんが、彼らのほとんどは、100% 確信していることを言うことを単に恐れていたのではないかと思います。ということは、管理上の問題である可能性が高いです。申し訳ありませんが、私はそれを修正する方法を知りません...

于 2010-10-08T15:52:55.130 に答える
2

m_str初期化リストで構築されます。したがって、それ自体に割り当てる時点では、完全には構築されていません。したがって、未定義の動作です。

(とにかく、その自己割り当ては何をすることになっていますか?)

于 2010-10-08T15:42:03.150 に答える
1

未定義の動作はクラッシュにつながる必要はありません-まったく問題がないかのように動作し続けることから、すぐにクラッシュすること、後で一見無関係な問題を引き起こす本当に奇妙なことをすることまで、ほとんど何でもできます。正規の主張は、「悪魔があなたの鼻から飛び出す」(別名「鼻の悪魔を引き起こす」)というものです。かつて、このフェーズの発明者は、「DeathStation 9000」で未定義の動作を引き起こした誰かから始まった核戦争について説明する(かなりクールな)Webサイトを持っていました。

編集:標準からの正確な表現は(§:1.3.12)です:

1.3.12未定義動作[defns.undefined]

この国際規格が要件を課していない、誤ったプログラム構成または誤ったデータの使用時に発生する可能性があるような動作。この国際規格が動作の明示的な定義の説明を省略している場合も、未定義の動作が予想される場合があります。[注:許容される未定義の動作は、予測できない結果で状況を完全に無視することから、環境に特徴的な文書化された方法で翻訳またはプログラムの実行中に動作すること(診断メッセージの発行の有無にかかわらず)、翻訳または実行の終了にまで及びます(診断メッセージの発行を伴う)。

于 2010-10-08T15:44:15.557 に答える
0

これは、

std::string str;
str = str;

std::string str(str);

前者は (ナンセンスですが) 機能しますが、後者は機能しません。これは、まだ構築されていないオブジェクトからオブジェクトをコピー構築しようとするためです。

もちろん、行く方法は

Test() : m_str() {}
于 2010-10-08T15:50:24.743 に答える