2016-06-13 12 views
4

Ich habe eine Klasse Struktur wie folgt:Overloading Verfahren in der Basisklasse mit Elementvariable als Standard

class Base { 
    public: 
     void setDefault(uint8_t my_default) { m_default = my_default; } 

     void method(uint8_t * subject) { method(subject, m_default); } 
     virtual void method(uint8_t * subject, uint8_t parameter) =0; 

    protected: 
     uint8_t m_default; 
}; 

class Derived1 : public Base { 
    public: 
     void method (uint8_t * subject, uint8_t parameter) { /* do something to subject */ } 
}; 

class Derived2 : public Base { 
    public: 
     void method (uint8_t * subject, uint8_t parameter) { /* do something different to subject */ } 
}; 

Ich möchte in der Lage sein method(...) auf jeder Instanz einer Klasse von Base, welche abgeleitet zu nennen nennen die in der abgeleiteten Klasse definierte Methode, die die Elementvariable als Standardparameter verwendet.

Von dem, was ich an anderer Stelle auf Stack Overflow here gelesen habe, muss die Überschreibung die gleiche Signatur haben, weshalb es nicht kompilieren wird.

Gibt es mögliche Unklarheiten bei diesem Ansatz?

Zwei Möglichkeiten, die ich denken kann, dies zu umgehen:

  1. einen Standard method(void) für jede abgeleitete Klasse erklären, aber die
  2. einen anderen Namen für die Standard-Methode verwenden, nicht sehr trocken erscheinen (zB defaultMethod(uint8_t * subject)) aber ich fühle, dass dies meine Klasse weniger intuitiv macht

Gibt es einen besseren Weg um dies?


Hier ist ein vollständiges Beispiel, die nicht kompiliert werden (Arduino IDE 1.7.9):

class Base { 
    public: 
     void setDefault(uint8_t my_default) { m_default = my_default; } 

     void method(uint8_t * subject) { method(subject, m_default); } 
     virtual void method(uint8_t * subject, uint8_t parameter) =0; 

    protected: 
     uint8_t m_default; 
}; 

class Derived1 : public Base { 
    public: 
     void method (uint8_t * subject, uint8_t parameter) { *subject += parameter; } 
}; 

class Derived2 : public Base { 
    public: 
     void method (uint8_t * subject, uint8_t parameter) { *subject *= parameter; } 
}; 

Derived1 derived1; 
Derived2 derived2; 

uint8_t subject = 0; 

void setup() { 
    // put your setup code here, to run once: 
    derived1.setDefault(3); 
    derived2.setDefault(5); 
} 

void loop() { 
    // put your main code here, to run repeatedly: 
    derived1.method(&subject, 1); // subject == 1 
    derived2.method(&subject, 2); // subject == 2 

    // won't compile with this line: 
    derived1.method(&subject); // subject == 5 
} 

Fehler produziert wird:

over-ride-load.ino: In function 'void loop()': 
over-ride-load.ino:39:29: error: no matching function for call to 'Derived1::method(uint8_t*)' 
over-ride-load.ino:39:29: note: candidate is: 
over-ride-load.ino:14:12: note: virtual void Derived1::method(uint8_t*, uint8_t) 
over-ride-load.ino:14:12: note: candidate expects 2 arguments, 1 provided 
Error compiling. 
+0

Wenn Sie Tippfehler und die Verwendung reservierter Schlüsselwörter (Standard in C++> 11) setzen, funktioniert il. Fügen Sie einfach eine virtuelle Überschreibung hinzu, um sie klarer zu machen, und Sie sind gut zu gehen. – Davidbrcz

+0

@Davidbrcz - danke, korrigierter Missbrauch des reservierten Wortes im Beispiel –

Antwort

3

Was ich glaube, dass Sie suchen, ist die using Direktive.

Sie haben in Base alles richtig gemacht. Die Verwendung eines Standardparameters ist (oft) besser als die Definition einer zweiten Funktion, wenn Ihre Anforderungen (mit einem Member) hier nicht möglich sind: Sie haben also eine zweite überladene Funktion definiert, um sie zu beheben (und sie definiert inline - Kudos!).

Aber das Problem kommt mit den abgeleiteten Klassen. Wenn Sie die virtuelle Funktion nicht überschreiben, wäre alles in Ordnung gewesen: Beide Versionen von method wären für Sie verfügbar. Aber Sie müssen es überschreiben, so dass Sie die Basisversion von method(subject); mit method(subject,parameter); effektiv "maskieren".

Was Sie tun möchten, ist "fördern" alle Basemethod s in die verschiedenen Derived s, um ihnen gleiche Gewichtung zu geben. Wie? Mit der using Direktive.

In jeder Derived Definition, setzen Sie den folgenden Code ein:

using Base::method; 

dass alle Base ‚s-Methode in die Derived "fördert" - und ermöglicht gleichzeitig einzelne außer Kraft zu setzen. Ich schlage vor, dass Sie diese Zeile direkt über jedem Derivedmethod() Override setzen.

+0

Das funktioniert - ich verstehe es noch nicht ganz, aber ich werde es nachlesen. TY. –

+0

Es ist das Konzept, die Definitionen einer Basisklasse mit den abgeleiteten gleichnamigen zu "verstecken", um zu vermeiden, dass sich der Compiler über zu viele Optionen beschwert, wenn er versucht, Ihre abgeleitete Klasse zu verwenden. Das funktioniert in der Theorie - bis es in die Quere kommt. Deshalb haben sie 'using' erfunden! –

2

Von dem, was ich Ich habe an anderer Stelle auf Stack Overflow gelesen, die Override muss die gleiche Signatur haben, weshalb sie nicht kompiliert wird.

Es wird kompiliert (wenn Sie die Syntaxfehler beheben). Es wird kompiliert, da die Überschreibung tut die gleiche Signatur haben:

virtual void method (uint8_t * subject, uint8_t parameter) =0; 

gegen

 void method(uint8_t * subject, uint8_t parameter) 

Auf der anderen Seite, Option 2 (anderer Name für Überlastung) noch ansprechender klingt. Überladungen können manchmal schwierig, verwirrend und gegensätzlich sein.


Aber auch wenn Ihr Beispiel richtig ist, was Sie vielleicht finden, dass folgende funktioniert nicht, was unlogisch sein könnte:

uint8_t b; 
Derived2 d2; 
d2.method(&b); 

Dies liegt daran, wie die Überladungsauflösung funktioniert. Ihre vorgeschlagene Option 2. löst dieses Problem trivialerweise, und deshalb empfehle ich Ihnen, dies zu tun. Andere Möglichkeiten, das Verfahren in Mutter zu nennen:

  • Anruf d2.Base::method(&b);
  • nur die einzelne Argument Verfahren über einen Basisobjektzeiger/reference nennen.
  • Fügen Sie für jede abgeleitete Klasse using Base::method; Deklaration hinzu. Dies wird in John Burgers Antwort ausführlicher beschrieben.
+0

Es war der letzte Teil, der nicht funktioniert hat, sorry meine ursprüngliche Frage hat das nicht klar gemacht. Ich habe mich für die 'using Base :: Methode;' entschieden, da sie mir die befriedigendste Lösung bietet, obwohl sie meinen Code ein wenig WET erscheint. Ty. –