1. ホーム
  2. c++

[解決済み] なぜ#include <string>はスタックオーバーフローエラーを防いでいるのですか?

2022-09-11 11:30:42

質問

これは私のサンプルコードです。

#include <iostream>
#include <string>
using namespace std;

class MyClass
{
    string figName;
public:
    MyClass(const string& s)
    {
        figName = s;
    }

    const string& getName() const
    {
        return figName;
    }
};

ostream& operator<<(ostream& ausgabe, const MyClass& f)
{
    ausgabe << f.getName();
    return ausgabe;
}

int main()
{
    MyClass f1("Hello");
    cout << f1;
    return 0;
}

コメントアウトすると #include <string> をコメントアウトしても、コンパイラエラーは発生しません。 #include <iostream> . もし私が 右クリック --> Go to Definition" をクリックすると、両方の行が同じ行を指しています。 xstring ファイル内の同じ行を指しています。

typedef basic_string<char, char_traits<char>, allocator<char> >
    string;

しかし、プログラムを実行すると、例外エラーが発生します。

OperatorString.exe の 0x77846B6E (ntdll.dll): 0xC00000FD: スタック オーバーフロー (Parameter: 0x00000001, 0x01202FC4)

をコメントアウトするときにランタイム エラーが発生する理由を考えてみてください。 #include <string> ? 私はVS 2013 Expressを使用しています。

どのように解決するのですか?

確かに、非常に興味深い動作ですね。

をコメントアウトするとランタイムエラーが発生するのですが、何か心当たりはありますか? #include <string>

MS VC++ コンパイラでエラーが発生するのは、以下のように #include <string> がないと operator<< に対して定義された std::string .

コンパイラがコンパイルしようとするとき ausgabe << f.getName(); をコンパイルしようとすると operator<< に対して定義された std::string . これは定義されていないので、コンパイラは代替手段を探します。そこには operator<< に対して定義された MyClass で、コンパイラはそれを使おうとするのですが、それを使うためには std::stringMyClass であり、これはまさに MyClass には明示的でないコンストラクタがあるからです! そのため、コンパイラは最終的に MyClass のインスタンスを生成し、それを再び出力ストリームに流そうとします。この結果、無限の再帰が発生します。

 start:
     operator<<(MyClass) -> 
         MyClass::MyClass(MyClass::getName()) -> 
             operator<<(MyClass) -> ... goto start;

このエラーを回避するためには #include <string> が存在することを確認します。 operator<< に対して定義された std::string . また MyClass のコンストラクタを明示的にして、このような予期せぬ変換を避けるようにしましょう。 知恵の輪:暗黙の変換を避けるために、引数を1つだけ取る場合はコンストラクタを明示的にします。

class MyClass
{
    string figName;
public:
    explicit MyClass(const string& s) // <<-- avoid implicit conversion
    {
        figName = s;
    }

    const string& getName() const
    {
        return figName;
    }
};

見た目は operator<< に対して std::string が定義されている場合のみ <string> が含まれる場合のみ定義され、そのためすべてがコンパイルされますが、次のような予期せぬ動作が発生します。 operator<< が再帰的に呼び出され MyClass を呼び出すのではなく operator<< に対して std::string .

を通してということでしょうか。 #include <iostream> の文字列は一部しか含まれないということでしょうか?

いいえ、stringは完全に含まれています。そうでなければ使うことができません。