44
votes

Comment `STD :: COUT` est-il implémenté?

std :: cout est une instance de std :: osstream . Je peux voir la déclaration de std :: cout dans un fichier nommé / usr / include / c ++ / 7 / iostream :

In file included from /opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/iostream:39:0,
                 from <source>:1:
/opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/ostream: In function 'int main()':
/opt/compiler-explorer/gcc-4.9.0/include/c++/4.9.0/ostream:384:7: error: 'std::basic_ostream<_CharT, _Traits>::basic_ostream() [with _CharT = char; _Traits = std::char_traits<char>]' is protected
       basic_ostream()
       ^
<source>:5:18: error: within this context
     std::ostream os;
                  ^

Et std :: osstream est défini par typedef std :: basic_ostream std :: osstream .

Quoi de plus, il semble que vous puissiez 't Créer une instance de std :: osstream . Voir cette démo Code Snippet :

#include<iostream>

int main()
{
    std::ostream os;
    return 0;
}


18 commentaires

Il peut se créer lui-même.


Vous pouvez créer une instance std :: osstream , mais vous ne pouvez pas la construire par défaut. Voir ici Pour en savoir plus sur les constructeurs. La page sur std :: cout a plus de détails sur la façon dont il est initialisé - Il s'agit d'un tampon de flux défini par l'implémentation auquel il est connecté, et la mise en œuvre doit également garantir qu'il est initialisé de manière appropriée au début du programme.


cout peut être créé comme une instance d'une classe qui dérive de std :: osstream


@DrewMCGowen mais cout est en fait une instance de std :: osstream autre que dérivé de std :: osstream .


@DrewmcGowen est-ce vraiment vrai? Le standard nécessite que la déclaration soit extern ostream cout; , ce qui semble empêcher cout d'être une référence à une classe dérivée.


Oups, j'ai menti - je pensais aux pointeurs, mais ici les pointeurs ne sont pas impliqués


@Nathanpierson Merci pour la réponse. Mais le lien ne semble pas très méchant. Il ne me dit pas clairement comment std :: cout est implémenté et comment std :: cout < / code> est créé par le constructeur de std :: osstream . Pour autant que je puisse voir, l'infomation la plus apparentée est les objets globaux std :: cout et std :: wcout de contrôle de contrôle vers un tampon de flux de type défini par l'implémentation (dérivé de std :: streambuf), associé à la standard c stream de sortie stdout. Et rien de plus.


La norme linguistique définit ce que vous voyez comme un programmeur et ce que vous obtenez. Il n'est pas censé vous dire comment il est mis en œuvre.


@John La spécification est délibérément vague ici pour donner aux implémenteurs de bibliothèque une certaine latitude dans la façon dont ils choisissent de coder les choses. Cela ressemble plus à un contrat qui dit ce qui doit être fait plutôt que de dire à quel point il faut faire spécifiquement.


Oui c'est correct. std :: basic_ostream a un constructeur qui prend un pointeur vers un objet std :: basic_streambuf . std :: cout est initialisé à l'aide d'un pointeur sur une instance d'une classe dérivée définie par l'implémentation de std :: basic_streambuf . Si vous voulez plus de détails, vous devez spécifier la mise en œuvre de la mise en œuvre.


@Nathanpierson je vois. Merci pour l'explication détaillée. Je travaille sur ubuntu . Après avoir lu votre dernière réponse, je pense que je comprends presque la déclaration de votre lien. Les objets globaux std :: cout et std :: wcout de contrôle de contrôle vers un tampon de flux de type défini par l'implémentation (dérivé de std :: streambuf),


Le nom du système d'exploitation ne nous dit pas le nom du compilateur C ++. Veuillez lire l'historique Edit pour voir pourquoi votre question a été modifiée.


Il semble que vous ne serez pas satisfait tant que vous ne voyez pas le code réel d'une implémentation de bibliothèque standard. Ainsi, vous voudrez peut-être parcourir le code de la bibliothèque standard GNU C ++ comme point de départ.


@ user207421 Je vois. Désolé, j'ai mis à jour le message. C'est GCC 4.9.


GCC 4.9 est plutôt vieux. Ne l'utilisez pas.


osstream * cout_ptr = :: new (cout) osstream (:: new (__ cout) __stdoutbuf (stdout, & mb_coout)); juste le placement nouveau dans le code d'initialisation.


@John C'est à l'implémentation comment implémenter le comportement spécifié par la norme. Il n'y a pas de réponse unique, il y a exactement ce que chaque implémentation a décidé de faire.


@ n.1.8e9-where's-my-sharem. C'est vraiment hors de mon contrôle. Je n'ai pas le choix, en effet.


3 Réponses :


17
votes

Un compilateur et sa mise en œuvre de la bibliothèque standard peuvent coopérer à l'aide de fonctionnalités non standard qui ne sont pas utilisables par de simples programmeurs.

Ce n'est pas nécessaire dans ce cas car il existe un constructeur public assez standard:

explicit basic_ostream(basic_streambuf<char_type, Traits>* sb);

Si vous avez un streambuf prêt, vous pouvez créer un objet de type osstream , tout comme la bibliothèque standard.

Qu'est-ce que streambuf est exactement un détail d'implémentation caché, mais sur une implémentation typique, c'est probablement un objet d'une classe personnalisée construite à partir de stdout (le style C pointeur de fichiers).


0 commentaires

41
votes

comment std :: cout est créé?

Tout d'abord, à partir de https://en.cpreference.com/w / cpp / io / ios_base / init :

std :: ios_base :: init

Cette classe est utilisée pour s'assurer que les flux C ++ par défaut (std :: cin, Std :: cout, etc.) sont correctement initialisés et détruits. [...]

L'en-tête se comporte comme s'il définit (directement ou indirectement) une instance de std :: ios_base :: init avec stockage statique Durée: [...]

meh, faisons un vrai exemple de code. J'utiliserai bibliothèque GCC C ++ . De https: // github.com/gcc-mirror/gcc/blob/master/libstdc%2b%2b-v3/include/std/iostream#l73 , c'est la partie importante:

#include <fstream>
#include <ext/stdio_sync_filebuf.h>
int main() {
    __gnu_cxx::stdio_sync_filebuf<char> mybuf_cout_sync(stdout);
    std::ostream os(&mybuf_cout_sync);
    os << "Hello world!\n";
    return 0;
}

4 commentaires

Hein. Donc dans globals_io.cc , std :: cin / std :: cout / std :: cerr ne sont rien mais char des tableaux alignés pour correspondre aux exigences de iStream / ostream . Pourtant, l'en-tête iostream et ios_init.cc les déclare comme des externes qui sont des objets réels isttream / en tant que tels (en supposant [correctement] ils n'ont pas été initialisés dans ce dernier cas, car il placement new est partout dans cette mémoire), et je suppose que le linker est d'accord avec cela parce qu'il se soucie juste Que la quantité correcte de mémoire alignée est là où elle s'attend à ce qu'elle soit? Bizarre.


@Shadowranger Les linkers, en règle générale, ne se soucient même pas de la taille et de l'alignement, bien qu'ils puissent théoriquement ... mais de nombreux compilateurs ne se soucient pas de remplir correctement les champs de taille dans les fichiers d'objet «Tables de symboles, les lieurs doivent donc y faire face.


@Shadowranger LTO s'en plaindre, mais seulement si vous créez une version statique de libstdc ++ avec LTO, qui est loin de la valeur par défaut. gcc.gnu.org/bugzilla/show_bug.cgi?id=59472 gcc.gnu.org/bugzilla/show_bug.cgi?id=64275


MSVC utilise une ruse similaire pour construire son cout , cin , et CERR , également; Je ne suis pas à jour sur les détails, cependant, la dernière fois que j'ai vérifié était la construction 2010 ou 2015.



-3
votes

Je pense qu'une partie de ce qui manque les réponses actuelles et de ce qui fait partie de votre question:
Le nom std :: cout est également «magique».
Cela signifie que la bibliothèque standard connaît à ce sujet et fournit les connexions nécessaires spécifiques au système d'exploitation au terminal; en utilisant le système respectif (et spécifique au système d'exploitation) pour la sortie, etc.


1 commentaires

Merci pour l'explication. Toute référence? Qu'est-ce que la tuyauterie de fond? Pourriez-vous s'il vous plaît expliquer cela plus en détail pour moi?