Pergunta

Vamos dizer que eu tenho a seguinte estrutura de classe:

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

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

Let também é dar um Car um identificador para a sua Engine. A FooCar será criado com um FooEngine* e uma BarCar será criado com um BarEngine*. Existe uma maneira de organizar as coisas de modo um objeto FooCar pode chamar funções de membro de FooEngine sem downcasting?

É aqui porque a estrutura de classe é colocado para fora do jeito que está agora:

  1. Todos Cars ter um Engine. Além disso, um FooCar só vai usar uma FooEngine.
  2. Existem dados e algoritmos compartilhados por todos os Engines que eu preferiria não copiar e colar.
  3. eu poderia querer escrever uma função que requer uma Engine de saber sobre a sua Car.

Assim que eu digitei dynamic_cast ao escrever este código, eu sabia que provavelmente estava fazendo algo errado. Existe uma maneira melhor de fazer isso?

UPDATE:

Com base nas respostas dadas até agora, eu estou inclinado para duas possibilidades:

  1. Tem Car fornecer uma função getEngine() virtual pura. Isso permitiria FooCar e BarCar ter implementações que retornam o tipo correto de Engine.
  2. absorver toda a funcionalidade Engine na árvore de herança Car. Engine foi quebrado por razões de manutenção (para manter as coisas Engine em um lugar separado). É um trade-off entre ter mais classes pequenas (pequena em linhas de código) versus tendo menos classes grandes.

Existe uma forte preferência comunidade para uma dessas soluções? Existe uma terceira opção que eu não tenha considerado?

Foi útil?

Solução

Estou assumindo que o carro possui um ponteiro Motor, e é por isso que você se encontra downcasting.

Leve o ponteiro fora de sua classe base e substituí-lo com uma função pura get_engine virtual (). Em seguida, seu FooCar e BarCar pode conter ponteiros para o tipo de motor correto.

(Edit)

por que isso funciona:

Desde o Car::get_engine() função virtual retornaria um referência ou um ponteiro , C ++ irá permitir que as classes derivadas para implementar esta função com um diferente tipo de retorno , enquanto o retorno digite apenas difere por ser um tipo mais derivado.

Isso é chamado covariant tipos de retorno , e permitirá que cada tipo Car para retornar o Engine correta .

Outras dicas

Apenas uma coisa que eu queria acrescentar:. Este projeto já cheira mal para mim por causa do que eu chamo de árvores paralelas

Basicamente, se você acabar com hierarquias de classe paralelas (como você tem com carro e do motor), então você está apenas pedindo para ter problemas.

Gostaria de repensar se Engine (e até mesmo carro) precisa ter subclasses ou aqueles são todos apenas diferentes instâncias das mesmas classes respectiva base.

Você também pode templatize a Motor da seguinte maneira

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

class FooCar : public Car<FooEngine>

class BarCar : public Car<BarEngine>

Eu não vejo por que um carro não pode ser composto por um motor (se BarCar sempre conterá BarEngine). Motor tem um muito forte relacionamento com o carro. Eu preferiria:

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

Seria possível para o FooCar usar o BarEngine?

Se não, você pode querer usar um AbstractFactory para criar o objeto direito carro, com o motor direito.

Você pode armazenar o FooEngine em FooCar, BarEngine em 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

O problema com esta abordagem é que a obtenção de um motor é uma chamada virtual (se você está preocupado com isso), e um método para definição um motor em Car exigiria downcasting.

Eu acho que isso depende se Engine só é usada em particular por Car e seus filhos ou se você também quiser usá-lo em outros objetos.

Se funcionalidades Engine não são específicos para Cars, eu usaria um método virtual Engine* getEngine() em vez de manter um ponteiro na classe base.

Se a sua lógica é específico para Cars, eu preferiria colocar os dados comum Engine / lógicas em um objeto separado (não necessariamente polimórfico) e manter FooEngine e implementação BarEngine em sua respectiva classe Car criança.

Quando a reciclagem implementação é mais necessária do que a herança interface, composição de objetos muitas vezes oferecem maior flexibilidade.

COM da Microsoft é uma espécie de desajeitado, mas ele tem um conceito novo - se você tem um ponteiro para uma interface de um objeto, você pode consultá-lo para ver se ele suporta quaisquer outras interfaces usando o função QueryInterface . A idéia é quebrar sua classe Motor em várias interfaces de modo que cada um pode ser usado de forma independente.

Permitir que eu não tenha perdido alguma coisa, isso deve ser bastante trivial.

A melhor maneira de fazer isso é criar funções virtuais puros em motores, que são então necessários em classes derivadas que estão a ser instanciado.

Uma solução crédito extra seria provavelmente ter uma interface chamada IEngine, que em vez passs para o seu carro, e cada função em IEngine é puro virtual. Você poderia ter um 'BaseEngine' que implementa algumas das funcionalidades que você quer (ou seja, 'partilhada') e depois ter as folhas fora dessa.

A interface é agradável que você nunca deve ter algo que você quer 'olhar' como um motor, mas provavelmente não é (ou seja, classes de teste de simulação e tal).

Existe uma maneira de organizar as coisas de modo um objeto FooCar pode chamar funções de membro de FooEngine sem downcasting?

Como esta:

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)
  {}
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top