为什么在这里避免堆栈溢出错误#include <string>?

这是我的示例代码:

#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; 

但是当我运行我的程序时,我得到一个exception错误:

OperatorString.exe中的0x77846B6E(ntdll.dll):0xC00000FD:堆栈溢出(参数:0x00000001,0x01202FC4)

任何想法为什么当注释掉#include <string>时出现运行时错误? 我正在使用VS 2013 Express。

的确,非常有趣的行为。

任何想法为什么我在注释掉#include <string>时出现运行时错误

使用MS VC ++编译器会发生错误,因为如果不包含#include <string> ,则不会为std::string定义operator<<

当编译器试图编译ausgabe << f.getName(); 它寻找一个为std::string定义的operator<< 。 由于没有定义,编译器寻找替代品。 有一个operator<<MyClass定义,编译器试图使用它,并使用它必须将std::string转换为MyClass ,这正是发生的事情,因为MyClass有一个非显式的构造函数! 所以,编译器最终会创build一个MyClass的新实例,并尝试再次将其stream式传输到输出stream。 这导致无尽的recursion:

  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<< for std::string只有在包含<string>时(使用MS编译器)才被定义,因此一切都会编译,但是由于operator<<正在recursion调用MyClass调用operator<< for std::string

这是否意味着通过#include <iostream>string仅被部分包含?

不,string是完全包含的,否则你将无法使用它。

问题是你的代码正在做一个无限recursion。 std::stringstd::ostream& operator<<(std::ostream&, const std::string&) )的stream操作符是在<string>头文件中声明的,尽pipestd::string本身是在其他头文件中声明的(由<iostream><string> )。

当你不包含<string> ,编译器试图find一种方法来编译ausgabe << f.getName();

碰巧你已经为MyClass定义了一个stream操作符,并且定义了一个允许std::string的构造函数,所以编译器使用它(通过隐式构造 ),创build一个recursion调用。

如果你声明了你的构造函数( explicit MyClass(const std::string& s) ),那么你的代码就不会再编译了,因为没有办法用std::string调用stream操作符,你将被迫包含<string>标题。

编辑

我的testing环境是VS 2010,从警告级别1( /W1 )开始,它会警告您这个问题:

警告C4717:'operator <<':在所有控制path上recursion,函数会导致运行时堆栈溢出