Почему #include предотвращает ошибку переполнения стека здесь?


это мой пример кода:

#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>. Если Я "щелкните правой кнопкой мыши --> перейти к определению" в Microsoft VS они оба указывают на одну и ту же строку в xstring file:

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

но когда я запускаю свою программу, я получаю ошибку исключения:

0x77846B6E (ntdll.dll) в OperatorString.exe: 0xC00000FD: переполнение стека (параметр: 0x00000001, 0x01202FC4)

любая идея, почему я получаю ошибку времени выполнения, если закомментировать #include <string>? Я использую VS 2013 Express.

2 118

2 ответа:

действительно, очень интересное поведение.

любая идея, почему я получаю ошибку времени выполнения При комментировании #include <string>

с компилятором MS VC++ ошибка происходит потому, что если вы не #include <string> у вас не будет operator<< определена в std::string.

когда компилятор пытается скомпилировать ausgabe << f.getName(); поиск operator<< определена в std::string. Поскольку он не был определен, компилятор ищет альтернативы. Существует operator<< определенными для MyClass и компилятор пытается использовать его, и использовать его для преобразования std::string до MyClass и это именно то, что происходит, потому что MyClass имеет неявный конструктор! Таким образом, компилятор в конечном итоге создает новый экземпляр вашего MyClass и пытается передать его снова в выходной поток. Это приводит к бесконечной рекурсии:

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

чтобы избежать ошибки вам нужно #include <string> чтобы убедиться, что есть operator<< определена в std::string. Также вы должны сделать ваш MyClass конструктор явный, чтобы избежать такого рода неожиданного преобразования. Правило мудрости: сделайте конструкторы явными, если они принимают только один аргумент, чтобы избежать неявного преобразования:

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> включен (с компилятором MS) и по этой причине все компилируется, однако вы получаете несколько неожиданное поведение как operator<< вызывается рекурсивно для MyClass вместо operator<< для std::string.

значит ли это, что через #include <iostream> строка включена только частично?

нет, строка полностью включена, иначе вы не сможете ее использовать.

проблема в том, что ваш код делает бесконечной рекурсии. Потоковый оператор для std::string (std::ostream& operator<<(std::ostream&, const std::string&)) объявляется в <string> заголовочный файл, хотя std::string сам объявляется в другом заголовочном файле (включенном обоими <iostream> и <string>).

когда вы не включите <string> компилятор пытается найти способ, чтобы скомпилировать ausgabe << f.getName();.

бывает, что вы определили оба оператора потоковой передачи для MyClass и конструктор, который признает std::string, Так компилятор использует его (через неявные конструкции), создавая рекурсивный вызов.

если вы объявляете explicit ваш конструктор (explicit MyClass(const std::string& s)) тогда ваш код больше не будет компилироваться, так как нет способа вызвать оператор потоковой передачи с помощью std::string, а вы будете вынуждены включить <string> заголовок.

EDIT

моя тестовая среда VS 2010, и начиная с уровня предупреждения 1 (/W1) это предупреждает Вас о проблема:

предупреждение C4717: 'operator