为什么#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;

但是当我运行我的程序时,出现异常错误:

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具有非显式构造函数! 因此,编译器最终会创建一个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<< for std::string只有在包含<string>时才会被定义(使用MS编译器),并且出于这个原因编译所有内容,但是由于operator<<正在递归调用MyClass调用operator<< for std::string

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

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


问题是您的代码正在进行无限递归。 std::stringstd::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:'operator <<':在所有控制路径上递归,函数将导致运行时堆栈溢出

链接地址: http://www.djcxy.com/p/24569.html

上一篇: Why is #include <string> preventing a stack overflow error here?

下一篇: Convert char to int in C and C++