Почему #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 ответа:
действительно, очень интересное поведение.
любая идея, почему я получаю ошибку времени выполнения При комментировании
#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