8
votes

Héritage et exploitant l'opérateur Ostream en C ++

J'ai essayé de trouver une réponse à cela, mais personne ne semble avoir exactement le même problème que moi.

Je travaille avec plusieurs classes dérivées. L'opérateur d'Ostream << pour chacun de ceux-ci devrait imprimer certaines choses communes à chacune, et certaines choses spécifiques à chacun. Plus tard, j'aimerais tirer davantage de ces classes dérivées, et encore une fois les nouvelles classes dérivées doivent imprimer certaines choses dans les "générations" au-dessus d'elles.
Par exemple:

la classe de base .h fichier xxx

le fichier base.cpp comprend ces lignes: xxx

puis je dérive: (firseDerive.h) xxx

fidufeuvre.cpp: xxx

alors je vouloir dériver: xxx

second.cpp: xxx

Je pense que le problème est probablement la déclaration dans le Très début du programme, ou les lignes comme base :: opérateur <<< / code>.

Une autre possibilité est que je ne le redécoupe pas dans le fichier de chaque classe héritée . Devrais-je être, et si oui, quelle syntaxe dois-je utiliser?

Il m'a été suggéré d'utiliser la méthode static_cast , mais mon professeur (celui qui a écrit la mission et ne nous donnera donc pas trop d'aide avec elle) a déclaré qu'il y a une meilleure façon de le faire. Toute suggestion?


1 commentaires

«Je pense que le problème est probablement ...» - Quels symptômes observez-vous? Erreur de compilation? Ligne, message? Ou comportement de temps d'exécution indésirable? Si oui, quoi et qu'attendez-vous?


6 Réponses :


6
votes

Vous ne pouvez pas implémenter l'opérateur << pour Ostreams en tant que membre de classe - il doit s'agir d'une fonction gratuite (éventuellement d'ami). C'est parce que dans l'expression: xxx

La chose sur le côté gauche du << ne sera pas une instance de votre classe, ce qui devrait être si c'était membre Fonction.

Appeler le parent de l'enfant - faire un statique_cast: xxx

que je pense est la "meilleure" solution. Sinon, donnez à vos classes un appel de fonction nommé impression () qui prend un système d'OStream comme paramètre et utilisez ceci pour implémenter votre opérateur <<. Cela entraînera beaucoup de code Cleer.


4 commentaires

J'ai donc besoin de le redéfinir comme «Code» Ostream & Opérateur << (Ostream & OS, Constons de base et base) '/ Code' Dans la base.h AMD, puis à nouveau comme «Code» Ostream & Operator << (Ostream & OS, Const de famille & Leçonnier) '/ code', etc.?


Ensuite, comment puis-je appeler l'opérateur de parents de l'enfant?


C'est exactement ce que OP essaie d'éviter. Le static_cast .


Bien que la mochette, dans ce cas, la fonction d'opérateur <<<< / code> est là pour être invoquée par l'opérateur de diffusion non membre (qui est correctement spécifié).



0
votes

Pour appeler une méthode de la classe de base, vous pouvez utiliser: xxx

mais opérateur <<< / code> est une fonction libre. La seule possibilité sans static_cast, je peux voir, c'est de définir l'opérateur comme un modèle, puis appelez explicitement la spécialisation comme celle-ci: xxx

Ceci pourrait être fait de la même manière pour l'opérateur < <, juste changer le nom fonction pour opérateur <<< / code> et ajouter les paramètres requis.


Une autre possibilité est de créer une fonction de membre virtuel: xxx

en classe dérivée Ceci pourrait appeler la fonction d'impression de base: xxx

alors il suffit d'avoir Opérateur <<< / Code> uniquement pour la classe et appellera la méthode d'impression appropriée polymorphicaly. Notez que l'opérateur <<< / code> est une fonction libre. xxx


1 commentaires

Ai-je besoin de redéclaré opérateur << dans chaque fichier d'en-tête de classe dérivé également, ou ne le remplaçait que chez l'enfant?



3
votes

Mis à part Qu'est-ce que @Neil dit, il serait probablement préférable de mettre en œuvre une méthode virtuelle dostream , de sorte que vous n'avez pas besoin de la possibilité de prélèvement: xxx

donc Vous n'avez besoin que de mettre en œuvre l'opérateur une fois. Vous pouvez également faire le opérateur <<< / code> non-ami et le dostream public, mais c'est probablement préférence personnelle.


5 commentaires

@Juraj: Eh bien, vous l'avez édité pendant que j'ai écrit la réponse, alors je ne le saurais pas.


Le code d'origine dans la question a déjà une fonction virtuelle dans la classe de base apprécie de dostream, étrangement appelé opérateur <<< / code>.


@Juraj, @Xeo - merci! S'il vous plaît laissez-moi savoir si j'ai bien ce droit - lorsque je veux réellement imprimer une instance de dérivée, la ligne que je veux sera STD :: Cout << dérivée; Et cela appellera à l'opérateur de base, qui appelle à son tour la fonction spécifiée pour chaque classe dérivée et à l'intérieur qu'elle utilise la fonction de base.


@Tony - Que suggérez-vous? Je n'ai pas bien compris votre commentaire à Neil. Je suis vraiment un débutant à cela.


@Biu: Eh bien, ce que Xeo et Martin suggèrent essentiellement la même chose que ce que vous aviez - avec la correction donnée dans ma réponse - sauf qu'ils évitent d'utiliser opérateur <<< / code> en faveur d'une fonction virtuelle bien nommée dans la classe de base. Dans l'ensemble, je pense qu'un identifiant approprié est moins cryptique qu'un opérateur utilisé de manière atypique, de sorte que les réponses de Xeo ou de Martin constituent une amélioration - aucune différence significative entre eux (d'où un +1 pour moi).



16
votes

Une technique simple pour cela est la suivante:

class Base
{  
    int FirstClassNumber;

    public:
        virtual void serialize(ostream& os) const
        {
             os << FirstClassNumber;
        }
};

// Implement the stream operator for the base class.
// All it does is call erialize which is a virtual method that
// will call the most derived version.
ostream& operator << (ostream& os, const Base &base)
{
    base.serialize(os);

    return os;
}

class FirstDerived:public Base
{  
    int SecondClassNumber;

    public:
        // Override serialize to make it call the base version.
        // Then output any local data.
        virtual void serialize(ostream& os) const
        {
             Base::serialize(os);
             os << SecondClassNumber;
        }
};


0 commentaires

1
votes

FRICKEREDED.CPP:

os << "The first number is: "; // finish streaming statement with ";"
Base::operator<<(os);   // separate statement to call this function...
os << "The second number is " << SecondClassNumber; // start streaming again


1 commentaires

Merci! Je vais jouer avec votre suggestion et celles de @juraj, @ Xeo et des autres commentateurs et voyez ce qui fonctionne le meilleur pour mon programme. Tous mes vœux:)



0
votes

Voici une légère modification de la réponse de Loki. Au lieu d'avoir une méthode de sérialisation virtuelle, j'utilise une méthode virtuelle to_string. Je pense que cela peut être réutilisé dans plus de contextes émettant à un ostream, qui pourrait être utile. XXX PRE>

produit P>

Base: 1 
FirstDerived: 1 2


0 commentaires