Question

Disons que j'ai la structure de classe suivante:

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

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

Donnons également à une voiture un descripteur pour son moteur . Un FooCar sera créé avec un FooEngine * et un BarCar sera créé avec un BarEngine * . Existe-t-il un moyen d'arranger les choses pour qu'un objet FooCar puisse appeler les fonctions membres de FooEngine sans déclassement?

Voici pourquoi la structure de classe est présentée telle qu'elle est actuellement:

  1. Tous les voitures ont un moteur . De plus, un FooCar utilisera uniquement un FooEngine .
  2. Il existe des données et des algorithmes partagés par tous les moteurs que je préférerais ne pas copier / coller.
  3. Je souhaiterais peut-être écrire une fonction nécessitant un moteur pour connaître son voiture .

Dès que j'ai tapé dynamic_cast en écrivant ce code, j'ai su que je faisais probablement quelque chose de mal. Y a-t-il une meilleure façon de faire cela?

MISE À JOUR:

Sur la base des réponses données jusqu'à présent, je suis orienté vers deux possibilités:

  1. Demandez à Car de fournir une fonction getEngine () virtuelle pure. Cela permettrait à FooCar et à BarCar de disposer d'implémentations renvoyant le type correct de Moteur .
  2. Absorber toutes les fonctionnalités Engine dans l'arborescence d'héritage Car . Engine a été mis hors service pour des raisons de maintenance (conserver les éléments Engine dans un endroit séparé). C'est un compromis entre avoir plus de petites classes (petites lignes de code) et moins de grandes classes.

Existe-t-il une préférence marquée de la part de la communauté pour l’une de ces solutions? Y a-t-il une troisième option que je n'ai pas envisagée?

Était-ce utile?

La solution

Je suppose que Car détient un pointeur sur le moteur, et c’est pourquoi vous vous retrouvez abattu.

Retirez le pointeur de votre classe de base et remplacez-le par une fonction virtuelle pure get_engine (). Ensuite, votre FooCar et votre BarCar peuvent contenir des pointeurs sur le type de moteur approprié.

(Modifier) ??

Pourquoi ça marche:

Etant donné que la fonction virtuelle Car :: get_engine () renverrait une référence ou un pointeur , C ++ autorisera les classes dérivées à implémenter cette fonction avec un différent. type de retour , tant que le type de retour ne diffère que par un type plus dérivé.

Ceci s'appelle types de retour covariant et permet à chaque voiture tapez pour renvoyer le moteur correct.

Autres conseils

Juste une chose que je voulais ajouter: cette conception sent déjà mauvais pour moi à cause de ce que j'appelle les arbres parallèles .

En gros, si vous vous retrouvez avec des hiérarchies de classes parallèles (comme avec Car et Engine), vous ne faites que poser des problèmes.

Je repenserais si Engine (et même Car) doit avoir des sous-classes ou si elles ne sont que des instances différentes des mêmes classes de base respectives.

Vous pouvez également définir le type de moteur comme suit

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

class FooCar : public Car<FooEngine>

class BarCar : public Car<BarEngine>

Je ne vois pas pourquoi une voiture ne peut pas être composée d'un moteur (si BarCar contient toujours BarEngine). Le moteur entretient une relation assez forte avec la voiture. Je préférerais:

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

Serait-il possible que la FooCar utilise le BarEngine?

Sinon, vous pouvez utiliser AbstractFactory pour créer le bon objet voiture, avec le bon moteur.

Vous pouvez stocker le FooEngine dans FooCar, BarEngine dans 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

Le problème avec cette approche est que l'obtention d'un moteur est un appel virtuel (si cela vous préoccupe), et une méthode pour définir un moteur dans Car . nécessiterait un downcasting.

Je pense que cela dépend si Engine n'est utilisé que de manière privée par Car et ses enfants ou si vous souhaitez également l'utiliser dans d'autres objets.

Si les fonctionnalités Engine ne sont pas spécifiques à Car , j'utiliserais une méthode de moteur virtuel * getEngine () au lieu de conserver un pointeur dans la classe de base.

Si sa logique est spécifique à Car , je préférerais placer les données / la logique Engine dans un objet séparé (pas nécessairement polymorphe) et conserver . FooEngine et BarEngine dans leur classe enfant Car respective.

Lorsque le recyclage des implémentations est plus important que l'héritage d'interface, la composition d'objets offre souvent une plus grande flexibilité.

Le COM de Microsoft est un peu maladroit, mais il a un nouveau concept. Si vous avez un pointeur sur une interface d’un objet, vous pouvez l’interroger pour savoir s’il prend en charge d’autres interfaces à l’aide de QueryInterface . L’idée est de diviser votre classe Engine en plusieurs interfaces afin que chacune puisse être utilisée indépendamment.

Si je n'ai rien oublié, cela devrait être plutôt trivial.

La meilleure façon de le faire est de créer des fonctions virtuelles pures dans Engine, qui sont ensuite requises dans les classes dérivées à instancier.

Une solution de crédit supplémentaire consisterait probablement en une interface appelée IEngine, que vous passerez plutôt à votre voiture, et chaque fonction dans IEngine est virtuelle. Vous pouvez avoir un "BaseEngine" qui implémente certaines des fonctionnalités souhaitées (c'est-à-dire "partagées"), puis des feuilles.

L’interface est agréable si vous voulez quelque chose qui ressemble à un moteur, mais ce n’est probablement pas le cas (c’est-à-dire des classes de tests fictifs, etc.).

  

Existe-t-il un moyen d'arranger les choses de sorte qu'un objet FooCar puisse appeler les fonctions membres de FooEngine sans réduire le temps de diffusion?

Comme ceci:

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)
  {}
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top