2016-07-19 2 views
1

Cette question a été rencontrée lors de la lecture de this section on learncpp.com. J'ai utilisé le code listé ici, puis j'ai fait de légères modifications pour les tests.Appel de constructeurs pendant l'héritage virtuel avec C++

Contexte

L'héritage virtuel crée une référence commune à la classe de base, ce qui a deux effets. Tout d'abord, il supprime l'ambiguïté car une seule copie des membres de la base est créée (par exemple, ajouter une fonction print() à PoweredDevice et l'appeler dans main() provoquerait sinon une erreur de compilation). Deuxièmement, la classe la plus dérivée devient responsable de l'appel du constructeur de base. Si l'une des classes intermédiaires tente d'invoquer le constructeur de base dans une liste d'initialisation, l'appel doit être ignored.

Le problème

Quand je compiler et exécuter le code, il retourne:

PoweredDevice: 3 
PoweredDevice: 3 
Scanner: 1 
PoweredDevice: 3 
Printer: 2 

Il devrait retourner:

PoweredDevice: 3 
Scanner: 1 
Printer: 2 

Quand je suis l'exécution en utilisant GDB (7,11 .1), il montre que les fonctions intermédiaires appellent aussi PoweredDevice via la liste d'initialisation - mais celles-ci doivent être ignorées. Cette initialisation multiple de PoweredDevice ne conduit pas à l'ambiguïté des membres, mais me perturbe car le code s'exécute plusieurs fois quand il ne devrait arriver qu'une seule fois. Pour un problème plus compliqué, je ne serais pas à l'aise avec l'héritage virtuel.

Pourquoi ces classes intermédiaires initialisent-elles toujours la base? Est-ce une bizarrerie avec mon compilateur (gcc 5.4.0) ou est-ce que je ne comprends pas comment l'héritage virtuel fonctionne?

Modifier: Code

#include <iostream> 
using namespace std; 

class PoweredDevice 
{ 
public: 
    int m_nPower; 
public: 
    PoweredDevice(int nPower) 
     :m_nPower {nPower} 
    { 
     cout << "PoweredDevice: "<<nPower<<endl; 
    } 
    void print() { cout<<"Print m_nPower: "<<m_nPower<<endl; } 
}; 

class Scanner : public virtual PoweredDevice 
{ 
public: 
    Scanner(int nScanner, int nPower) 
     : PoweredDevice(nPower) 
    { 
     cout<<"Scanner: "<<nScanner<<endl; 
    } 
}; 

class Printer : public virtual PoweredDevice 
{ 
public: 
    Printer(int nPrinter, int nPower) 
     : PoweredDevice(nPower) 
    { 
     cout<<"Printer: "<<nPrinter<<endl; 
    } 
}; 

class Copier : public Scanner, public Printer 
{ 
public: 
    Copier(int nScanner, int nPrinter, int nPower) 
     :Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower} 
    { } 
}; 

int main() 
{ 
    Copier cCopier {1,2,3}; 
    cCopier.print(); 
    cout<<cCopier.m_nPower<<'\n'; 
    return 0; 
} 
+2

quelles "légères altérations" avez-vous faites? De toute façon vous devriez poster le code ici – user463035818

+2

Bienvenue sur Stack Overflow! S'il vous plaît ** [edit] ** votre question avec un [mcve] ou [SSCCE (court, autonome, exemple correct)] (http://sscce.org) – NathanOliver

+0

Ajout du code dans une édition.Les variations que j'ai vérifiées incluaient les valeurs et fonctions des membres dans la classe de base, y compris les différents spécificateurs d'accès. J'ai également essayé de changer les niveaux d'accès à l'héritage virtuel. –

Répondre

7

Cela semble être un bug de GCC, déclenché lorsque l'initialisation uniforme est utilisé avec l'héritage virtuel.


Si nous changeons:

Copier(int nScanner, int nPrinter, int nPower) 
    :Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower} 
{ } 

à:

Copier(int nScanner, int nPrinter, int nPower) 
    :Scanner (nScanner, nPower), Printer (nPrinter, nPower), PoweredDevice (nPower) 
{ } 

L'erreur disparaît, et il se comporte comme prévu:

PoweredDevice: 3 
Scanner: 1 
Printer: 2 
Print m_nPower: 3 
3 

Clang et Visual Studio sont capables de compiler correctement le code d'origine et de donner la sortie attendue.

+0

Cela a fonctionné. Je vous remercie! –

+2

@RyanB De rien. Si vous voulez vérifier si quelque chose est un bogue dans un compilateur spécifique, ou un problème de langue, vous pouvez généralement utiliser des environnements en ligne pour les compilateurs que vous n'avez pas installés. J'aime [Rextester] (http://www.rextester.com/) pour cela, il a Clang, GCC et VC++ disponibles dans le menu déroulant. Chacun d'entre eux se trouve sur une page séparée, vous devriez donc copier votre code avant de passer à un autre. –

+0

Est-ce que quelqu'un a déjà fait un bug pour ça? Sinon, nous devrons continuer à souffrir. –