it-swarm.com.de

Warum verhindert #include <string> hier einen Stapelüberlauffehler?

Dies ist mein Beispielcode:

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

Wenn ich #include <string> Auskommentiere, erhalte ich keine Compiler-Fehlermeldung, weil es irgendwie durch #include <iostream> Enthalten ist. Wenn ich "Rechtsklick -> Gehe zur Definition" in Microsoft VS zeigen beide auf dieselbe Zeile in der xstring -Datei:

typedef basic_string<char, char_traits<char>, allocator<char> >
    string;

Aber wenn ich mein Programm starte, bekomme ich einen Ausnahmefehler:

0x77846B6E (ntdll.dll) in OperatorString.exe: 0xC00000FD: Stapelüberlauf (Parameter: 0x00000001, 0x01202FC4)

Irgendeine Idee, warum ich einen Laufzeitfehler erhalte, wenn ich #include <string> Auskommentiere? Ich verwende VS 2013 Express.

121
airborne

In der Tat sehr interessantes Verhalten.

Jede Idee, warum ich bekomme ich Laufzeitfehler beim Auskommentieren #include <string>

Beim MS VC++ - Compiler tritt der Fehler auf, weil Sie ohne #include <string> Kein operator<< Für std::string Definiert haben.

Wenn der Compiler versucht, ausgabe << f.getName(); zu kompilieren, sucht er nach einem operator<<, Der für std::string Definiert ist. Da es nicht definiert wurde, sucht der Compiler nach Alternativen. Es ist ein operator<< Für MyClass definiert, und der Compiler versucht, es zu verwenden, und um es zu verwenden, muss er std::string In MyClass konvertieren, und das ist genau das Was passiert, weil MyClass einen nicht expliziten Konstruktor hat? Der Compiler erstellt also eine neue Instanz von MyClass und versucht, sie erneut in Ihren Ausgabestream zu streamen. Dies führt zu einer endlosen Rekursion:

 start:
     operator<<(MyClass) -> 
         MyClass::MyClass(MyClass::getName()) -> 
             operator<<(MyClass) -> ... goto start;

Um den Fehler zu vermeiden, müssen Sie mit #include <string> Sicherstellen, dass für operator<< Ein std::string Definiert ist. Außerdem sollten Sie Ihren Konstruktor MyClass explizit angeben, um diese Art der unerwarteten Konvertierung zu vermeiden. Weisheitsregel: Machen Sie Konstruktoren explizit, wenn sie nur ein Argument verwenden, um eine implizite Konvertierung zu vermeiden:

class MyClass
{
    string figName;
public:
    explicit MyClass(const string& s) // <<-- avoid implicit conversion
    {
        figName = s;
    }

    const string& getName() const
    {
        return figName;
    }
};

Es sieht so aus, als ob operator<< Für std::string Nur definiert wird, wenn <string> Enthalten ist (mit dem MS-Compiler). Aus diesem Grund wird alles kompiliert, Sie erhalten jedoch ein etwas unerwartetes Verhalten wie operator<< Wird rekursiv für MyClass aufgerufen, anstatt operator<< Für std::string Aufzurufen.

Bedeutet das, dass die Zeichenfolge #include <iostream> Nur teilweise enthalten ist?

Nein, die Zeichenfolge ist vollständig enthalten, andernfalls können Sie sie nicht verwenden.

162
Pavel

Das Problem ist, dass Ihr Code eine unendliche Rekursion durchführt. Der Streaming-Operator für std::string (std::ostream& operator<<(std::ostream&, const std::string&)) ist in der Header-Datei <string> Deklariert, obwohl std::string Selbst in einer anderen Header-Datei deklariert ist (enthalten in beiden <iostream> Und <string>).

Wenn Sie <string> Nicht angeben, versucht der Compiler, ausgabe << f.getName(); zu kompilieren.

Es kommt vor, dass Sie sowohl einen Streaming-Operator für MyClass als auch einen Konstruktor definiert haben, der einen std::string Zulässt. Daher verwendet der Compiler diesen Operator (durch implizite Konstruktion ) und erstellt einen rekursiver Aufruf.

Wenn Sie explicit Ihren Konstruktor (explicit MyClass(const std::string& s)) deklarieren, wird Ihr Code nicht mehr kompiliert, da es nicht möglich ist, den Streaming-Operator mit std::string Aufzurufen, und Sie ' Der Header <string> muss angegeben werden.

[~ # ~] edit [~ # ~]

Meine Testumgebung ist VS 2010, und ab Warnstufe 1 (/W1) Werden Sie vor dem Problem gewarnt:

warnung C4717: 'Operator <<': Auf allen Steuerpfaden rekursiv, Funktion führt zum Überlauf des Runtime-Stacks

35
cbuchart