首页 文章

为什么#include <string>会阻止堆栈溢出错误?

提问于
浏览
119

这是我的示例代码:

#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 回答

  • 34

    确实,非常有趣的行为 .

    在评论#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> 以确保为 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::stringoperator<< 才会定义,因此一切都会编译,但是由于 operator<< 被递归调用 MyClass 而不是为 std::string 调用 operator<< ,因此会出现一些意外行为 .

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

    不,完全包含字符串,否则您将无法使用它 .

  • 160

    问题是你的代码正在进行无限递归 . std::stringstd::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 )开始,它会警告您这个问题:

    警告C4717:'operator <<':在所有控制路径上递归,函数将导致运行时堆栈溢出

相关问题