Frage

Lassen Sie uns sagen, dass ich die folgende Klassenstruktur:

class Car;
class FooCar : public Car;
class BarCar : public Car;

class Engine;
class FooEngine : public Engine;
class BarEngine : public Engine;

Lassen Sie uns auch ein Car einen Griff in seine Engine geben. Ein FooCar wird mit einem FooEngine* und einem BarCar erstellt wird mit einem BarEngine* erstellt werden. Gibt es eine Möglichkeit, Dinge zu organisieren, so ein FooCar Objekt Elementfunktionen FooEngine ohne einziehe anrufen kann?

Hier ist, warum die Klassenstruktur ist es die Art und Weise angelegt ist jetzt:

  1. Alle Cars haben eine Engine. Des Weiteren wird ein FooCar immer nur eine FooEngine verwenden.
  2. Es gibt Daten und Algorithmen, die von allen Engines geteilt, die ich lieber nicht kopieren und einfügen.
  3. Ich könnte möchte eine Funktion schreiben, die eine Engine erfordert über seine Car kennen.

Sobald ich dynamic_cast getippt, wenn Sie diesen Code zu schreiben, wusste ich, dass ich wohl etwas falsch zu machen war. Gibt es einen besseren Weg, dies zu tun?

UPDATE:

Auf der Grundlage der Antworten bisher gegeben, ich bin Neigung in Richtung zwei Möglichkeiten:

  1. Haben Sie Car bieten eine rein virtuelle getEngine() Funktion. Das würde erlauben FooCar und BarCar Implementierungen zu haben, die die richtige Art von Engine zurück.
  2. absorbieren alle die Engine Funktionalität in den Car Vererbungsbaum. Engine wurde aus Wartungsgründen ausgebrochen (um die Engine Sachen in einem separaten Ort). Es ist ein Kompromiss zwischen mehreren kleinen Klassen mit (kleinen in Codezeilen) im Vergleich zu weniger große Klassen mit.

Gibt es eine starke Gemeinschaft Präferenz für eine dieser Lösungen? Gibt es eine dritte Möglichkeit habe ich nicht in Betracht gezogen?

War es hilfreich?

Lösung

Ich gehe davon aus, dass Auto einen Motor Zeiger hält, und das ist, warum Sie finden sich einziehe.

Nehmen Sie den Zeiger aus Ihrer Basisklasse und ersetzen sie durch eine rein virtuelle get_engine () Funktion. Dann wird Ihr FooCar und Barcar können Zeiger auf den richtigen Motortyp halten.

(Edit)

Warum dies funktioniert:

Da die virtuelle Funktion Car::get_engine() zurückkehren würde eine Referenz oder einen Zeiger , C ++ wird Klassen ermöglichen abgeleitet diese Funktion mit einem verschiedenen Rückgabetyp zu implementieren , solange die Rückkehr Typ unterscheidet sich nur durch eine mehr abgeleiteten Typ zu sein.

Das heißt kovarianten Rückgabetypen , und wird jede Car Art erlaubt den richtigen Engine zurückzukehren .

Andere Tipps

Nur eines wollte ich hinzufügen:. Dieser Entwurf schon schlecht riecht mich wegen dem, was ich nenne parallel Bäume

Im Grunde genommen, wenn Sie mit parallelen Klassenhierarchien am Ende (wie man mit Auto und Motor hat), dann sind Sie nur Ärger bringen.

Ich würde überdenken, wenn Engine (und sogar Auto) Subklassen haben muss oder das sind alles nur verschiedene Instanzen der gleichen entsprechenden Basisklassen.

Sie können auch die Motorart templatize wie folgt

template<class EngineType>
class Car
{
    protected:
        EngineType* getEngine() {return pEngine;}
    private:
        EngineType* pEngine;
};

class FooCar : public Car<FooEngine>

class BarCar : public Car<BarEngine>

Ich sehe nicht, warum ein Auto nicht von einem Motor zusammengesetzt werden kann (wenn Barcar enthält immer BarEngine). Motor hat eine ziemlich starke Beziehung mit dem Auto. Ich würde es vorziehen:

class BarCar:public Car
{
   //.....
   private:
     BarEngine engine;
}

Wäre es möglich, für die FooCar die BarEngine zu benutzen?

Wenn nicht, mögen Sie vielleicht einen Abstrakte verwenden, um das richtige Auto-Objekt zu erstellen, mit dem richtigen Motor.

Sie können speichern Sie die FooEngine in FooCar, BarEngine in Barcar

class Car {
public:
  ...
  virtual Engine* getEngine() = 0;
  // maybe add const-variant
};

class FooCar : public Car
{
  FooEngine* engine;
public:
  FooCar(FooEngine* e) : engine(e) {}
  FooEngine* getEngine() { return engine; }
};

// BarCar similarly

Das Problem bei diesem Ansatz ist, dass ein Motor immer ein virtuelles Call ist (wenn Sie darüber besorgt sind), und ein Verfahren zum Einstellung ein Motor in Car Downcasting erfordern würde.

Ich denke, es hängt ab, wenn Engine nur privat von Car und seinen Kindern oder wenn Sie es auch wollen verwendet wird, in anderen Objekten verwenden.

Wenn Engine Funktionalitäten nicht spezifisch für Cars sind, würde ich eine virtual Engine* getEngine() Methode verwenden, anstatt einen Zeiger in der Basisklasse zu halten.

Wenn seine spezifische Logik Cars ist, würde ich es vorziehen, die gemeinsame Engine Daten / Logik in einem separaten Objekt (nicht notwendigerweise polymorphe) und halte FooEngine und BarEngine Umsetzung in ihrer jeweiligen Car Kind Klasse setzen.

Bei der Umsetzung Recycling nötiger als Schnittstelle Vererbung ist, Objekt Zusammensetzung bietet oft mehr Flexibilität.

Microsofts COM ist eine Art klobig, aber es hat ein neuartiges Konzept haben - wenn Sie einen Zeiger auf eine Schnittstelle eines Objekts haben, können Sie es abfragen, um zu sehen, ob es irgendwelche anderen Schnittstellen unterstützt mit der Query-Interface Funktion. Die Idee ist, Ihre Maschine Klasse in mehrere Schnittstellen zu brechen, so dass jeweils unabhängig voneinander verwendet werden können.

Zulassen ich etwas nicht verpasst haben, sollte dies eher trivial sein.

Der beste Weg, dies zu tun, ist reine virtuelle Funktionen in Maschine zu schaffen, die in abgeleiteten Klassen dann erforderlich sind, die instanziiert werden sollen.

Eine zusätzliche Kredit Lösung wäre wahrscheinlich eine Schnittstelle haben genannt iEngine, dass Sie stattdessen Ihr Auto passs, und jede Funktion in iEngine ist rein virtuell. Sie könnten einen ‚BaseEngine‘ haben, dass einige der Funktionen, die Sie wollen (das heißt, ‚geteilt‘) und dann haben Blatt diejenigen aus der das implementiert.

Die Schnittstelle ist schön, sollten Sie immer etwas, das Sie wollen, müssen wie ein Motor zu ‚Look‘, aber wahrscheinlich nicht (das heißt, Mock Testklassen und so weiter).

  

Gibt es eine Möglichkeit, Dinge zu organisieren, so ein FooCar Objekt Elementfunktionen FooEngine ohne einziehe anrufen kann?

Wie folgt aus:

class Car
{
  Engine* m_engine;
protected:
  Car(Engine* engine)
  : m_engine(engine)
  {}
};

class FooCar : public Car
{
  FooEngine* m_fooEngine;
public:
  FooCar(FooEngine* fooEngine)
  : base(fooEngine)
  , m_fooEngine(fooEngine)
  {}
};
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top