2012-03-29 3 views
2

Also habe ich überall gesucht und ich kann nicht scheinen, die Antwort zu dieser spezifischen Frage zu finden. Ich benutze ein winXP mit Cygwin und gcc 3.4.4 cygming special.Inherited-Klasse, die eine benutzerdefinierte Klasse unter Verwendung eines Nicht-Standardkonstruktors initialisiert

Problem: Ich habe eine Klasse, die als Schnittstelle mit einigen abstrakten Methoden und geschützten Variablen funktioniert, die in jeder Klasse sein sollten, die von dieser Klasse erbt. Jetzt habe ich auch eine andere Klasse, die eine Mitgliedsvariable dieser Schnittstelle ist.

class Bar { 
private: 
    int y; 
public: 
    Bar(int why); 
}; 

Bar::Bar(int why) : y(why) {} 

class Foo { 
protected: 
    Bar b; 
public: 
    Foo(int x); 
    virtual void print_base(); 
}; 

Foo::Foo(int x) : b(x+3) // Have to use initializer list here. 
{ 
    //this->b(x+3); // doesn't work 
} 

class DerFoo : public Foo { 
protected: 
    Bar db; 
public: 
    DerFoo(int x); 
}; 

DerFoo::DerFoo(int x) : Foo(x), 
    db(x+3) // (Bar)(int) being called, works fine 
    // db(4.0, 30) // no matching function for call to Bar::Bar(double, int) 
    // note: candidates are Bar::Bar(const Bar&), Bar::Bar(int) 
    // b(x-3) // doesn't work class DerFoo does not have any field named 'b' 
{ 
    //this->b(x - 3); // Doesn't work, error no match for call to (Bar)(int) 
    //this->db(x + 3); // Doesn't work, error no match for call to (Bar)(int) 
} 

So ist das Problem, wie Sie ist innerhalb der abgeleiteten Klasse foo sehen können, DerFoo wie b zu initialisieren. Ich habe versucht, Mitglied Initialisierung Methode, aber dann erkennt der Compiler nicht über geschützte Variablen. Aus irgendeinem mir unbekannten Grund kann der Konstruktor in dieser Klasse nicht gefunden werden. Auch wenn ein "falscher" Aufruf an den Konstruktor einer geschützten Membervariable (nicht geerbt) eingeschlossen wäre, würde dies die korrekte Version des Konstruktors vorschlagen.

Ich habe noch keine Ahnung, wie das geht. Jede Hilfe wird sehr geschätzt.

+0

Ich bin verwirrt - mit ein paar kleineren Verbesserungen dieses kompiliert gut auf ideone (http://ideone.com/2cLUa). Welchen Fehler bekommst du und wo? – tmpearce

+2

Ok, ich denke ich sehe wo du verwirrt wirst. In dem von Ihnen geposteten Code wird die 'Foo :: b'-Membervariable ** ** initialisiert, wenn Sie' Foo (x) 'aufrufen. Sie können dem Konstruktor "Bar" eine Debug-Nachricht hinzufügen, und Sie werden sehen, dass sie zweimal initialisiert wird - einmal für "b" und einmal für "db". Sobald "b" initialisiert ist, können Sie nicht neu initialisieren, Sie müssen ihm einen neuen Wert zuweisen, indem Sie '=' (Zuweisungsoperator) verwenden. – tmpearce

+0

Rechts. Vielen Dank für Ihre Antworten, sehr aufschlussreich und hilfreich. Da einige meinen, dies sei "sehr ineffizient", hat mein realer Code viele Variablen auf der Schnittstellenebene. Das könnte ein schlechtes Design von mir sein, aber ich denke nicht, dass es für dieses Projekt wichtig ist. Deshalb denke ich, dass ich mit dieser Wahl gehen, 'Foo (int b, int c, double d) ...' für alle meine Membervariablen und dann tun 'DerFoo (int dera, Derx): Foo (b, c, d) {... ' – tomasgudm

Antwort

2

DerFoo ‚s Konstrukteure müssen nicht (und kann) initialisieren b, das ist Foo Job. Die Konstruktoren DerFoo sind dafür verantwortlich, nur die unmittelbaren Unterobjekte DerFoo zu initialisieren, nämlich db und die Foo, die eine Basisklasse von DerFoo ist. Foo 's Konstruktor wiederum ist verantwortlich für die Initialisierung b.

Die Abfolge der Ereignisse ist wie:

  • DerFoo 's Konstruktor ruft Foo' s Konstruktor
  • Foo 's Konstruktor ruft b' s Konstruktor
  • Foo ‚s Konstruktor läuft seinen Körper, so dass Das Foo Objekt wurde komplett aufgebaut
  • DerFoo 's Konstruktor Invoke db 's Konstruktor
  • DerFoo' s Konstruktor führt seinen Körper und ließ das DerFoo Objekt vollständig aufgebaut.

Wenn im DerFoo Konstruktor Sie den Wert nicht wie, dass der Foo Konstruktor in b links, können Sie einen neuen Wert b zuweisen, eine dieser Syntaxen mit:

b = Bar(47); 
this->b = Bar(47); 
this->Foo::b = Bar(47); 
Foo::b = Bar(47); 
+0

Vielen Dank für diese Antwort, sehr anschaulich. – tomasgudm

3

Nachdem Sie eine Variable deklariert haben, müssen Sie sie setzen, sonst werden Sie sie wie eine Funktion aufrufen.

this->b = Bar(x+3); 

Der bevorzugte Weg ist, um die Initialisiererliste zu verwenden, um unnötige Kopien von Bar zu vermeiden. Wenn Sie jedoch b außerhalb des Konstruktors setzen müssen, wird das obige Beispiel verwendet.

+0

Danke, ich dachte, ich hätte das versucht, aber offensichtlich verhinderten andere Bugs in meinem Code (den ich jetzt repariert habe), dass es zu dieser Zeit funktionierte. Ich war verrückt, danke dafür. – tomasgudm

+0

Dies erzeugt zweimal "Balken", was in der beschriebenen Situation absolut ineffizient ist. – LiKao

+0

Dies erstellt mindestens dreimal eine "Bar". Einmal für "b", einmal für "db" und einmal während der Zuweisung. –

0

b ist in der Foo-Klasse. um darauf zuzugreifen (sicher) verwenden

Foo::b = Bar(x-3); 
+0

'b' ist nicht statisch, daher macht der Bereichsoperator hier keinen Sinn. 'this -> b 'oder nur' b' ist was hier benötigt wird.Dies erklärt überhaupt nicht, warum 'this-> b (x-x)' nicht funktioniert (es ist ein Funktionsaufruf statt einer Zuweisung). – LiKao

+0

macht es Sinn. Was machst du, wenn es 2 Superklassen mit dem gleichen Feld "b" gibt? Hör auf, deine Ignoranz zu zeigen. – UmNyobe

+0

Es gibt keine zwei Felder "b". Es gibt ein Feld "b" in "Foo" und ein Feld "db" in "DerFoo". Das zusätzliche Scoping macht also keinen Sinn, weil es nichts disambiguiert. Außerdem haben Sie den Funktionsaufruf ohne weitere Erwähnung stillschweigend in eine tatsächliche Zuweisung umgewandelt ... Die von Ihnen beschriebene Lösung ist ein Fehler, aber die korrekte Korrektur (die Sie auch in Ihrem Code vorgenommen haben) wird nicht mit einem einzigen Wort erwähnt. Daher der Downvote. SO dient nicht nur dazu, richtigen Code zu geben, sondern auch zu erklären. – LiKao

-1

Sie haben keine haben eine Initialisiererliste zu verwenden, Sie sollten auf jeden Fall auch an dieser Stelle eine Initialisiererliste verwenden.

Bei der Konstruktion eines Objekts, bevor der Code des Konstruktors eingegeben wird, sind alle Elementvariablen bereits konstruiert. Wenn Sie keine Initialisierer angeben, werden sie standardmäßig erstellt.

Auch Sie können eine Variable nicht erneut konstruieren, nachdem sie bereits konstruiert wurde. Ihr

this->b(x+3) 

sagt nicht die Kompilierung b zu konstruieren, ist es zu sagen, eine Funktion b auf dem Objekt mit dem Namen zu nennen. Eine solche Funktion existiert in Ihrer Klasse nicht, daher der Fehler. Beachten Sie, dass es nicht möglich ist, den Konstruktor einmal für diese Variable aufzurufen, wenn ein Objekt einmal in eine Variable umgewandelt wurde (nur um den Wert zu ändern).

Ein Wert kann mit = wie in den meisten Sprachen geändert werden. Daher könnten Sie tun:

Foo::Foo(int x) 
{ 
    this->b = Bar(x+3); 
} 

Dies bedeutet, dass Sie eine andere namenlos Bar Objekt erstellen und es ist Wert zu this->b zuweisen. Sie sollten beachten, dass dies bedeutet, dass Sie beim Erstellen eines Foo zwei Objekte Bar erstellen. Zuerst der Standard, der vorher erstellt wurde. Der Konstruktorcode wird eingegeben, dann der neue namenlose. Dann weisen Sie den Wert schließlich dem bereits konstruierten Objekt zu, sodass dieser Code viel ineffizienter ist als der, der Initialisierungslisten verwendet.

EDIT:

Da ich die zweite doesn't work in der oben stehenden Code hier einige zusätzliche Informationen verpasst:

Sie sind auch b direkt im Konstruktor des abgeleiteten DerFoo Objekt zu initialisieren versuchen. Sobald dieser Teil des Codes jedoch erreicht ist, wurde er bereits erstellt. Daher kommt jeder Versuch, es im abgeleiteten Konstruktor zu konstruieren, zu spät.

Daher müssen Sie entweder Foo einen weiteren Konstruktor hinzufügen, der den Wert übernimmt und diesen in Ihrem Konstruktor DerFoo verwenden. Diese Lösung ist vorzuziehen, da sie das Objekt Bar nur einmalig in b aufbauen wird. Wenn Sie einen solchen Konstruktor nicht hinzufügen können, müssen Sie eine Zuweisung im Konstruktorcode für DerFoo verwenden.

Der Versuch, b direkt im DerFoo Konstruktor zu initialisieren, funktioniert nicht, selbst wenn der Bereichsoperator verwendet wird.

DerFoo::DerFoo() : Foo::b(x-3) {} 

noch einen Fehler erzeugen: http://ideone.com/6H8ZD

+0

Es scheint, er will b in der Unterklasse initialisieren. Also wirklich helfen Sie nicht – UmNyobe

+0

und stoppen Sie Ihre serielle Downvoting ... Joe und ich waren richtig. – UmNyobe

+0

@UmNyobe - Es ist mir nicht klar, dass er * in der Unterklasse "b" initialisieren will. Ich glaube, er versteht falsch, dass er * b * in der Unterklasse initialisieren muss. –

1

Ich finde die Frage nicht sehr klar, aber lass uns sehen, ob ich verstanden habe, was du zu tun versuchst und wie.

DerFoo::DerFoo(int x) : Foo(x), [a] 
    db(x+3) 
    // db(4.0,30)   [1] 
    // note: candidates are Bar::Bar(const Bar&), Bar::Bar(int) 

    // b(x-3)    [2] 
{ 
    //this->b(x - 3);  [3] 
    //this->db(x + 3);  [4] 
} 

Der erste Fehler ist [1], wo der Compiler Sie sagt, dass es keinen Konstruktor Bar ist, die sowohl ein Doppel- und ein int nimmt. Der Fehler listet auch die zwei möglichen Konstruktoren auf, die Sie verwenden können: Bar(int), Bar(Bar const &). Ich bin unsicher, was Sie mit dieser Linie beabsichtigten, aber Sie haben bereits herausgefunden (vorherige Zeile), dass durch die Bereitstellung einer int der Anruf funktioniert.

[2] b ist kein Mitglied von DerFoo und kann daher nicht in der Initialisierungsliste von DerFoo initialisiert werden. Es ist die Verantwortlichkeit von Foo, sein eigenes Mitglied zu initialisieren, und das wird durch den Aufruf des Foo-Konstruktors in [a] geschehen.

[3], [4], beide Ausdrücke haben die Form this->member(i). Während der Initialisierung wird die Syntax member(i) gut initialisiert member mit dem Wert i. Außerhalb der Initialisierung bedeutet die Syntax operator()(int) den Wert i übergeben. Diese Mitglieder wurden bereits initialisiert, aber wenn Sie sie zurücksetzen möchten, müssen Sie zuweisen anstatt sie zu initialisieren.

+0

Danke für Ihre Antwort. Wie Sie sehen können, sind sie alle auskommentiert, eine Möglichkeit für mich, Methoden zu zeigen, die ich ausprobiert habe und weiß, dass sie scheitern. Aber vielen Dank dafür, dass es sehr klar ist :) – tomasgudm

Verwandte Themen