Это мой пример кода:
#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
файле:
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
Но когда я запускаю свою программу, я получаю ошибку исключения:
0x77846B6E (ntdll.dll) в OperatorString.exe: 0xC00000FD: переполнение стека (параметр: 0x00000001, 0x01202FC4)
Есть идеи, почему я получаю сообщение об ошибке выполнения при комментировании #include <string>
? Я использую VS 2013 Express.
c++
string
stack-overflow
explicit
бортовой
источник
источник
#include<iostream>
и<string>
оба могут включать<common/stringimpl.h>
....\main.cpp(23) : warning C4717: 'operator<<': recursive on all control paths, function will cause runtime stack overflow
при запуске этой строкиcl /EHsc main.cpp /Fetest.exe
Ответы:
Действительно, очень интересное поведение.
С компилятором MS VC ++ ошибка возникает, потому что, если вы этого не сделаете,
#include <string>
вы неoperator<<
определите дляstd::string
.Когда компилятор пытается скомпилировать,
ausgabe << f.getName();
он ищетoperator<<
определенный дляstd::string
. Поскольку он не был определен, компилятор ищет альтернативы. Существуетoperator<<
определенный for,MyClass
и компилятор пытается его использовать, и чтобы использовать его, он должен преобразоватьstd::string
в,MyClass
и это именно то, что происходит, потому что уMyClass
него неявный конструктор! Итак, компилятор создает новый экземпляр вашегоMyClass
и пытается снова передать его в ваш выходной поток. Это приводит к бесконечной рекурсии:Чтобы избежать ошибки, вам необходимо
#include <string>
убедиться, чтоoperator<<
дляstd::string
. Также вы должны сделать свойMyClass
конструктор явным, чтобы избежать такого неожиданного преобразования. Правило мудрости: сделайте конструкторы явными, если они принимают только один аргумент, чтобы избежать неявного преобразования:Похоже , что
operator<<
дляstd::string
получает определенный только тогда , когда<string>
включен (с компилятором MS) и по этой причине все откомпилировалось, однако вы получите несколько неожиданное поведение,operator<<
становится вызывается рекурсивно дляMyClass
вместо вызоваoperator<<
дляstd::string
.Нет, строка включена полностью, иначе вы не сможете ее использовать.
источник
std::string
без него#include<string>
могут произойти все, не ограничиваясь ошибкой времени компиляции. Другой вариант - вызов неправильной функции или оператора.Проблема в том, что ваш код выполняет бесконечную рекурсию. Оператор потоковой передачи для
std::string
(std::ostream& operator<<(std::ostream&, const std::string&)
) объявлен в<string>
файле заголовка, хотяstd::string
сам объявлен в другом файле заголовка (включенном как в, так<iostream>
и в<string>
).Если вы не включаете
<string>
компилятор, он пытается найти способ скомпилироватьausgabe << f.getName();
.Бывает, что вы определили как оператор потоковой передачи, так
MyClass
и конструктор, допускающий astd::string
, поэтому компилятор использует его (посредством неявной конструкции ), создавая рекурсивный вызов.Если вы объявите
explicit
свой конструктор (explicit MyClass(const std::string& s)
), ваш код больше не будет компилироваться, поскольку нет возможности вызвать оператор потоковой передачи с помощьюstd::string
, и вы будете вынуждены включить<string>
заголовок.РЕДАКТИРОВАТЬ
Моя тестовая среда - VS 2010, и начиная с уровня предупреждения 1 (
/W1
) она предупреждает вас о проблеме:источник