这是我的示例代码:
#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>
包括't get any compiler error, I guess because it' . 如果我在Microsoft VS中"right-click --> Go to Definition",它们都指向 xstring
文件中的同一行:
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
但是当我运行我的程序时,我收到一个异常错误:
OperatorString.exe中的0x77846B6E(ntdll.dll):0xC00000FD:堆栈溢出(参数:0x00000001,0x01202FC4)
知道为什么我在评论 #include <string>
时出现运行时错误?我正在使用VS 2013 Express .
2 回答
确实,非常有趣的行为 .
使用MS VC编译器时会发生错误,因为如果不
#include <string>
,则不会为std::string
定义operator<<
.当编译器尝试编译
ausgabe << f.getName();
时,它会查找为std::string
定义的operator<<
. 由于未定义,编译器会寻找替代方案 . 有operator<<
为MyClass
定义,并且编译器尝试使用它,并且要使用它,它必须将std::string
转换为MyClass
,这正是发生的事情,因为MyClass
有一个非显式构造函数!因此,编译器最终会创建MyClass
的新实例,并尝试将其再次流式传输到输出流 . 这导致无休止的递归:为了避免错误,您需要
#include <string>
以确保为std::string
定义operator<<
. 此外,您应该明确MyClass
构造函数以避免这种意外转换 . 智慧规则:如果构造函数只采用一个参数来避免隐式转换,则使构造函数显式化:只有当包含
<string>
(使用MS编译器)时,std::string
的operator<<
才会定义,因此一切都会编译,但是由于operator<<
被递归调用MyClass
而不是为std::string
调用operator<<
,因此会出现一些意外行为 .不,完全包含字符串,否则您将无法使用它 .
问题是你的代码正在进行无限递归 .
std::string
(std::ostream& operator<<(std::ostream&, const std::string&)
)的流操作符在<string>
头文件中声明,尽管std::string
本身在其他头文件中声明(由<iostream>
和<string>
包括) .当您不包含
<string>
时,编译器会尝试查找编译ausgabe << f.getName();
的方法 .碰巧你已经为
MyClass
定义了一个流操作符和一个允许std::string
的构造函数,所以编译器使用它(通过implicit construction),创建一个递归调用 .如果您声明
explicit
您的构造函数(explicit MyClass(const std::string& s)
),那么您的代码将不再编译,因为无法使用std::string
调用流操作符,并且您将被强制包含<string>
标头 .EDIT
我的测试环境是VS 2010,从警告级别1(
/W1
)开始,它会警告您这个问题: