2016-07-19 22 views
1

Dies ist eine Frage, die ich beim Lesen this section on learncpp.com begegnete. Ich habe den hier aufgelisteten Code verwendet und dann geringfügige Änderungen zum Testen vorgenommen.Aufrufen von Konstruktoren während der virtuellen Vererbung mit C++

Hintergrund

Virtuelle Vererbung schafft eine gemeinsame Referenz auf die Basisklasse, die zwei Effekte hat.

Zuerst entfernt es Mehrdeutigkeit, da nur einmal Kopie der Mitglieder der Basis erstellt werden (z. B. Hinzufügen einer print() - Funktion zu PoweredDevice und Aufrufen von es in main() würde andernfalls einen Compilerfehler verursachen).

Zweitens ist die am weitesten abgeleitete Klasse verantwortlich für den Aufruf des Basiskonstruktors. Wenn eine der Zwischenklassen versucht, den Basiskonstruktor in einer Initialisierungsliste aufzurufen, sollte der Aufruf ignored sein.

Das Problem

Wenn ich kompilieren und den Code ausführen, gibt sie:

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

Es zurückkehren sollte:

PoweredDevice: 3 
Scanner: 1 
Printer: 2 

Wenn ich folge der Ausführung GDB mit (7.11 .1), zeigt es, dass die Zwischenfunktionen auch PoweredDevice über die Initialisierungsliste aufrufen - diese sollten jedoch ignoriert werden. Diese mehrfache Initialisierung von PoweredDevice führt nicht zur Mehrdeutigkeit von Membern, macht mir aber Probleme, weil Code mehrfach ausgeführt wird, wenn er nur einmal vorkommen sollte. Für ein komplizierteres Problem wäre es mir nicht bequem, virtuelle Vererbung zu verwenden.

Warum initialisieren diese Zwischenklassen noch immer die Base? Ist es eine Eigenart mit meinem Compiler (gcc 5.4.0) oder missverstehe ich, wie die virtuelle Vererbung funktioniert?

bearbeiten: 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

was "leichte Änderungen" haben Sie gemacht? Trotzdem solltest du den Code hier posten – user463035818

+2

Willkommen bei Stack Overflow! Bitte ** [edit] ** Ihre Frage mit einem [mcve] oder [SSCCE (kurz, eigenständiges, korrektes Beispiel)] (http://sscce.org) – NathanOliver

+0

Der Code in einer Bearbeitung hinzugefügt.Von mir überprüfte Varianten enthielten Elementwerte und Funktionen in der Basisklasse, einschließlich verschiedener Zugriffsspezifizierer. Ich habe auch versucht, virtuelle Vererbung Zugriff Ebenen zu ändern. –

Antwort

7

Dies scheint ein GCC Fehler zu sein, dann ausgelöst, wenn einheitliche Initialisierung mit virtueller Vererbung verwendet wird.


Wenn wir ändern:

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

zu:

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

Der Fehler verschwindet, und es verhält sich wie erwartet:

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

Sowohl Clang als auch Visual Studio können den ursprünglichen Code ordnungsgemäß kompilieren und die erwartete Ausgabe bereitstellen.

+0

Das hat funktioniert. Vielen Dank! –

+2

@RyanB Gern geschehen. Wenn Sie überprüfen möchten, ob es sich um einen Fehler in einem bestimmten Compiler oder um ein Sprachproblem handelt, können Sie normalerweise Onlineumgebungen für Compiler verwenden, die Sie nicht installiert haben. Ich mag [Rextester] (http://www.rexttester.com/) dafür, es hat Clang, GCC und VC++ im Drop-Down-Menü verfügbar. Jeder befindet sich jedoch auf einer separaten Seite, daher sollten Sie Ihren Code kopieren, bevor Sie zu einem anderen wechseln. –

+0

Hat jemand jemals einen Fehler dafür eingereicht? Sonst müssen wir einfach weiter leiden. –