2008-11-24 17 views
6

Mein C++ - Framework verfügt über Schaltflächen. Ein Button stammt von Control. Eine Funktion, die ein Control akzeptiert, kann also einen Button als Argument verwenden. So weit, ist es gut.C++ - Vorlagen und Vererbung

Ich habe auch Liste < T>. Die Liste < Schaltfläche> stammt jedoch nicht von der Liste < Steuerung> ab, was bedeutet, dass eine Funktion, die eine Liste von Steuerelementen akzeptiert, keine Liste von Schaltflächen als Argument verwenden kann. Das ist unglücklich.

Vielleicht ist dies eine dumme Frage, aber ich sehe nicht, wie kann ich diese lösen :(Liste < Knopf > von Liste < Steuer > ableiten sollte, aber ich sehe keinen Weg, dies automatisch geschehen lassen“ “.

+0

OK, mein Schlechter. Ich habe Listen von Zeigern, nicht Listen von Objekten. Und natürlich würde das Speichern meiner Button * s in einer Liste von Control * s das Funktionsaufrufproblem beheben, und das mache ich normalerweise, außer wenn ich eine Liste von Button * s benötige, um sie als Button zu verwenden, nicht als Control. – ggambett

+0

Nun, was hast du? A Liste und Liste oder nur eine Liste ? Ich bin verwirrt. Bitte geben Sie die Angelegenheit :) –

+0

Wenn Sie nur eine Liste haben, und wollen spezielle Fall einzelne Elemente, wenn sie von Button-Typ sind, werden Sie gezwungen, Dynamic_cast Mate zu verwenden. –

Antwort

6

Ich hasse es zu sagen, aber wenn Sie eine Liste von Instanzen zu Control anstelle von Zeigern zu Control verwenden, werden Ihre Schaltflächen sowieso Müll (Google "Objekt Slicing"). Wenn es sich um Listen von Zeigern handelt, dann machen Sie entweder list<button*> in list<control*>, wie andere vorgeschlagen haben, oder kopieren Sie eine neue list<control*> aus der list<button*> und übergeben Sie stattdessen diese in die Funktion. Oder schreiben Sie die Funktion als Vorlage neu.

Also, wenn Sie zuvor eine Funktion haben doSomething genannt, die eine Liste von Kontrollen als Argument hat, würden Sie es umschreiben als:

template <class TControl> 
void doSomething(const std::list<TControl*>& myControls) { 
    ... whatever the function is currently doing ... 
} 

void doSomethingElse() { 
    std::list<Button*> buttons; 
    std::list<Control*> controls; 
    doSomething(buttons); 
    doSomething(controls); 
} 
+0

Hmm, das könnte funktionieren. Es sieht jedoch etwas hässlich aus :) – ggambett

+0

Es sieht hässlich aus, aber es ist das Herz der Standardvorlagenbibliothek und der modernen C++ Programmierung. Überprüfen Sie den Quellcode zu std :: list sich irgendwann. Nicht hübsch. – Michel

+0

Mein Hauptproblem dabei ist, dass es (IIUC) völlig legal ist, dass der Compiler Bit für Bit identische Funktionen für jeden Typ erzeugt, der verwendet wird. Naja. – BCS

6

Wie wäre es mit Zeigern? haben Sie einfach eine Liste von list<Control*> und setzen, was Kontrolle abgeleitete Objekte, die Sie in sie mögen.

+0

Wenn Sie sich entscheiden, eine Liste von Zeigern zu verwenden, müssen Sie daran denken, die Objekte zu löschen, wenn Sie damit fertig sind (Sie verlieren die automatische Speicherverwaltung) . Um es zurückzuholen, siehe Boosts ptr_container-Bibliothek. Es hat einen Container, der eine Liste von Zeigern verwaltet, als wäre es eine Standardliste. –

2

Statt Liste mit < Knopf >, Verwendung Liste < Steuerung * >, das sind zeigt auf Buttons. So hat Ihre Funktion nur um einen Typ zu nehmen: Liste < Kontrolle * >.

+0

Entschuldigung, dass ich nicht klar bin. Ich benutze Zeiger (Smart Pointer, eigentlich). Die Funktion übernimmt eine Liste der Control *. Die Sache ist, ich möchte es eine Liste von Button * übergeben. – ggambett

7

Stroustrup hat einen Artikel zu diesem Thema in seiner FAQ:

Why can't I assign a vector<Apple*> to a vector<Fruit*>

Sie es auf zwei Arten lösen:

  • Machen Sie die Liste Zeiger auf Control enthalten. Dann akzeptieren Sie List<Control*>
  • Machen Sie Ihre Funktion zu einer Vorlage. Sie können immer noch List<Button> und List<Control> verwenden, aber es ist mehr Vorabcode und nicht die meiste Zeit notwendig.

Hier ist Code für die zweite Alternative. Die erste Alternative wird bereits von anderen Antworten erklärt:

class MyWindow { 
    template<typename T> 
    void doSomething(List<T> & l) { 
     // do something with the list... 
     if(boost::is_same<Control, T>::value) { 
      // special casing Control 

     } else if(boost::is_same<Button, T>::value) { 
      // special casing Button 

     } 

    } 
}; 

doSomething für List<derived from Control>, einige mehr Code nur zu beschränken, ist (suchen Sie nach enable_if wenn Sie wissen wollen) benötigt.

Beachten Sie, dass diese Art von Code (suchen, welchen Typ Sie haben) eher zu vermeiden ist. Sie sollten solche Dinge mit virtuellen Funktionen behandeln. Fügen Sie zu Control eine Funktion doSomething hinzu, und überschreiben Sie es in Button.

0

Im Allgemeinen ist die C++ Art und Weise Algorithmen zu schreiben, die auf einer Liste ausführen , Sequenz, ...ist es, Iteratoren als Argumente bereitzustellen.

template < class iterator > 
doSomething(iterator beg, iterator end); 

Dies löst die Liste < Taste *> nicht aus Liste abgeleitet < Steuerung *>. (unter Verwendung einer Vorlage Liste < T *> würde auch, aber das ist irgendwie Ihre Funktion Generika-aber nicht-wirklich)

Meiner Erfahrung nach kann gute Template-Funktionen auf Iteratoren arbeiten eine Menge (zu viel ...) arbeiten, aber es ist die "C++ Art und Weise" ...

Wenn Sie diese Route gehen, erwägen Sie die Verwendung Boost.ConceptCheck. Es wird dein Leben viel einfacher machen.