121 votes

Pourquoi est-ce que #include <string> empêcher une erreur de débordement de pile ici?

C'est mon exemple de code:

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

Si j'en commentaire, #include <string> je n'ai aucune erreur de compilation, j'imagine, parce que c'est une sorte de reprise par #include <iostream>. Si je "clic-droit --> Aller à la Définition de" dans Microsoft VS ils insistent tous les deux sur la même ligne dans l' xstring le fichier:

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

Mais quand je lance mon programme, j'obtiens une erreur d'exception:

0x77846B6E (ntdll.dll) OperatorString.exe: 0xC00000FD: débordement de Pile (Paramètre: 0x00000001, 0x01202FC4)

Aucune idée de pourquoi j'obtiens une erreur d'exécution lors de commenter #include <string>? Je suis l'aide de VS 2013 Express.

161voto

Pavel Points 1715

En effet, très intéressante comportement.

Aucune idée de pourquoi je reçois je erreur d'exécution lors de commenter #include <string>

Avec MS VC++ compilateur que cette erreur se produit parce que si vous n' #include <string> vous n'aurez pas operator<< défini pour std::string.

Lorsque le compilateur essaie de compiler ausgabe << f.getName(); il cherche une operator<< défini pour std::string. Depuis, il n'a pas été défini, le compilateur cherche des solutions de rechange. Il y a un operator<< défini pour MyClass et le compilateur essaie de l'utiliser, et pour l'utiliser il doit convertir std::string de MyClass , et c'est exactement ce qui se passe parce qu' MyClass a un non-explicite du constructeur! Ainsi, le compilateur, finit par créer une nouvelle instance de votre MyClass et tente de le diffuser à nouveau à votre flux de sortie. Il en résulte une récursivité sans fin:

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

Pour éviter l'erreur, vous avez besoin d' #include <string> à assurez-vous qu'il y a un operator<< défini pour std::string. Aussi, vous devriez faire de votre MyClass constructeur explicite afin d'éviter ce genre de inattendus de conversion. La règle de la sagesse: faire des constructeurs explicites si ils ne prendre qu'un seul argument pour éviter de conversion implicite:

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

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

Il ressemble operator<< pour std::string est défini uniquement lorsque <string> est inclus (avec le MS compilateur) et, pour cette raison, tout compile, cependant, vous obtenez un peu comportement inattendu en operator<< est appelé de manière récursive pour MyClass au lieu de l'appeler operator<< pour std::string.

Est-ce à dire que, grâce à #include <iostream> chaîne de caractères est inclus seulement en partie?

Non, la chaîne est entièrement inclus, sinon vous ne seriez pas capable de l'utiliser.

35voto

cbuchart Points 91

Le problème, c'est que votre code est en train de faire une récursion infinie. La diffusion en continu de l'opérateur de std::string (std::ostream& operator<<(std::ostream&, const std::string&)) est déclaré en <string> fichier d'en-tête, bien qu' std::string lui-même est déclarée dans d'autres fichier d'en-tête (fourni par les deux <iostream> et <string>).

Lorsque vous n'incluez pas d' <string> le compilateur essaie de trouver un moyen pour compiler ausgabe << f.getName();.

Il se passe que vous avez défini à la fois un streaming opérateur de MyClass et un constructeur qui admet une std::string, de sorte que le compilateur utilise (dans l' implicite de la construction), la création d'un appel récursif.

Si vous déclarez explicit votre constructeur (explicit MyClass(const std::string& s)) puis votre code ne compile pas plus, car il n'y a aucun moyen d'appeler l'opératrice streaming avec std::string, et vous serez forcé d'inclure l' <string> - tête.

MODIFIER

Mon environnement de test est par rapport à 2010, et de commencer à l'alerte de niveau 1 (/W1) il vous met en garde sur le problème:

avertissement C4717: "opérateur<<": récursive sur tous les chemins de contrôle, la fonction sera la cause de débordement de la pile d'exécution

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X