这是我的示例代码:
#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>
包含的。如果我在 VS 中"右键单击 --> 转到定义">Microsoft它们都指向xstring
文件中的同一行:
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
但是当我运行我的程序时,我收到一个异常错误:
运算符字符串.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<<
。由于它未定义,编译器会寻找替代项。有一个为MyClass
定义的operator<<
,编译器尝试使用它,并且要使用它,它必须将std::string
转换为MyClass
这正是发生的事情,因为MyClass
有一个非显式构造函数!因此,编译器最终会创建MyClass
的新实例,并尝试将其再次流式传输到输出流。这会导致无休止的递归:
start:
operator<<(MyClass) ->
MyClass::MyClass(MyClass::getName()) ->
operator<<(MyClass) -> ... goto start;
为避免错误,您需要#include <string>
以确保为std::string
定义了operator<<
。此外,还应显式MyClass
构造函数以避免这种意外转换。 智慧法则:如果构造函数只接受一个参数以避免隐式转换,则使其显式
class MyClass
{
string figName;
public:
explicit MyClass(const string& s) // <<-- avoid implicit conversion
{
figName = s;
}
const string& getName() const
{
return figName;
}
};
看起来只有当包含<string>
(与 MS 编译器一起)时才定义std::string
的operator<<
,因此一切都会编译,但是您会遇到一些意外的行为,因为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>
标头。
编辑
我的测试环境是VS 2010,从警告级别1(/W1
)开始,它会警告您该问题:
警告 C4717:"运算符<<":在所有控制路径上递归,函数将导致运行时堆栈溢出