60 votes

comment fonctionne réellement le cout << ?

Je me demandais comment std::cout est capable d'utiliser << comme il le fait.

Ma principale interrogation est de savoir si std::cout comme une instance de quelque chose. Fondamentalement, comment << défini ? Si je fais cela pour une classe personnalisée, j'ai besoin d'une instance en quelque sorte...

Je pourrais envisager de l'implémenter comme une sorte de hack avec des pointeurs void ou quelque chose comme ça, mais j'aimerais voir la façon dont c'est réellement fait.

Est-ce que quelqu'un ici le sait ? Merci

0 votes

Si vous voulez vraiment tout savoir sur iostreams, vous pouvez jeter un coup d'œil à une copie de amazon.com/Standard-IOStreams-Locales-Programmers-Reference/dp/

18 votes

El cout << y cin >> Les expressions sont les raisons pour lesquelles le C++ est si peu intuitif et difficile à apprendre.

17 votes

@Mehrdad : Si vous choisissez un langage en fonction de la façon dont on écrit à la console, vous choisissez le langage pour les mauvaises raisons. Je suis d'accord avec le iostream est complexe, mais il y a beaucoup d'aspects géniaux du langage qui ne sont pas du tout liés à iostream (par exemple, la STL). (Si vous n'aimez pas iostream, alors ne l'utilisez pas. Utilisez les éléments de <cstdio> ou des éléments spécifiques à votre plate-forme).

59voto

Billy ONeal Points 50631

std::cout est une instance de std::ostream . std::cout << "something" appelle l'un des operator<< surcharges comme cela serait fait pour toute instance de std::ostream .

Elle est "spéciale" dans la mesure où elle fait référence à la console, mais sinon elle se comporte exactement comme une ofstream ou un ostringstream serait.

EDIT : Le chaînage fonctionne comme pour tous les autres opérateurs. Considérez :

class MyType
{
    friend std::ostream& operator<<(std::ostream& target, const MyType& source);
    int val;
public:
    MyType()
        : val(0)
    { }
    MyType& Add(int toAdd)
    {
        val += toAdd;
        return *this;
    }
};

MyType& operator+(MyType& target, int toAdd)
{
    return target.Add(toAdd);
}

std::ostream& operator<<(std::ostream& target, const MyType& source)
{
    target << source.val;
    return target; //Make chaining work
}

int main()
{
    MyType value1;
    value1 + 2 + 3 + 4;
    std::cout << value1 << " and done!" << std::endl;
}

Dans ce cas, le chaînage pour +s sur MyType fonctionnent pour la même raison que les << en plus du travail de l std::ostream . Les deux sites + y << sont associatifs à gauche, ce qui signifie qu'ils sont évalués de gauche à droite. Dans le cas des opérateurs surchargés, l'opérateur est remplacé par l'appel de fonction équivalent.

EDIT2 : Un peu plus en détail :

Disons que tu es le compilateur et que tu analyses les données.

std::cout << value1 << " and done!" << std::endl;

D'abord, << est associatif à gauche, donc on commence à gauche. Vous évaluez le premier << et le transformer en appel de fonction :

operator<<(std::cout, value1) << " and done!" << std::endl;

Vous voyez alors que vous avez à nouveau un std::ostream (le résultat de l'appel à operator<< ), et un char * que vous transformez à nouveau en appel de fonction :

operator<<(operator<<(std::cout, value1)," and done!") << std::endl;

et ainsi de suite jusqu'à ce que vous ayez traité l'ensemble de la déclaration.

2 votes

+1 Mais (pour me faire l'avocat), comment fonctionne le chaînage ? (par exemple std::cout << a << b ... )

0 votes

Est-ce que ça renvoie aussi un std::ostream ? Je veux dire que vous pouvez aussi faire cout << "quelque chose" << "autre chose". Comment fonctionne le deuxième opérateur<< ?

6 votes

@pst : Tous les operator<< Les surcharges sont de la forme std::ostream& operator<<(std::ostream& target, TYPE& source) et ils renvoient la référence passée en tant que source. C'est-à-dire, std::cout << a << b; est la même chose que operator<<(operator<<(std::cout, a), b);

0voto

Joshua Points 1

Et iostream. Réfléchissons un instant. Un flux est un objet avec un tampon qui peut contenir plusieurs types de données et contrôler l'entrée et la sortie de lui-même. Et un tampon est juste un nombre "D'APPUIS DE TOUCHES QUE LE FLUX CONTIENT" , ou 1 morceau de tampon est n'importe quel type de données "par exemple une classe ou un int est toujours 1 morceau de tampon. L'entrée et la sortie de la fenêtre MS_DOS est un scénario vraiment bizarre. Cela se fait avec un pointeur void. En termes simples, il ne pointe vers rien. Mais c'est utile car il peut contrôler un objet avec facilité. Pourquoi ? Parce qu'il peut pointer vers n'importe quoi. Si vous vous penchez sur l'API WIN32, vous entendrez souvent le mot handle. Mais, en fait, ce n'est rien d'autre qu'un pointeur vide. Il n'occupe pas de mémoire et peut faire référence à n'importe quoi, même en dehors de ce langage. Les interfaces graphiques en sont le meilleur exemple. Vous pouvez faire apparaître une fenêtre WIN32 de nulle part en utilisant une API multiplateforme. C'est bizarre mais ça marche.

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