문제

다음 수업 구조가 있다고 가정 해 봅시다.

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

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

또한 봅시다 Car 그것에 대한 손잡이 Engine. ㅏ FooCar a FooEngine* 그리고 a BarCar a BarEngine*. 물건을 정리할 수있는 방법이 있습니까? FooCar 객체는 멤버 함수를 호출 할 수 있습니다 FooEngine 다운 캐스팅없이?

다음은 수업 구조가 지금의 방식대로 배치 된 이유입니다.

  1. 모두 Cars가 있습니다 Engine. 또한, a FooCar a FooEngine.
  2. 모두가 공유하는 데이터 및 알고리즘이 있습니다 Engine오히려 복사하고 붙여 넣지 않을 것입니다.
  3. 필요한 함수를 작성하고 싶을 수도 있습니다. Engine 그것에 대해 알고 있습니다 Car.

내가 입력하자마자 dynamic_cast 이 코드를 쓸 때, 나는 아마도 내가 뭔가 잘못하고 있다는 것을 알았습니다. 더 좋은 방법이 있습니까?

업데이트:

지금까지 주어진 답변을 바탕으로 저는 두 가지 가능성에 기대고 있습니다.

  1. 가지다 Car 순수한 가상을 제공하십시오 getEngine() 기능. 그것은 허용 할 것입니다 FooCar 그리고 BarCar 올바른 종류를 반환하는 구현을 갖습니다 Engine.
  2. 모든 것을 흡수하십시오 Engine 기능에 대한 기능 Car 상속 트리. Engine 유지 보수상의 이유로 세분화되었습니다 (보관하기 위해 Engine 별도의 장소에있는 물건). 더 작은 클래스 (작은 코드 라인)를 갖는 것과 큰 클래스가 적은 것 사이의 상충 관계입니다.

이러한 솔루션 중 하나에 대한 강력한 커뮤니티 선호도가 있습니까? 내가 고려하지 않은 세 번째 옵션이 있습니까?

도움이 되었습니까?

해결책

나는 자동차가 엔진 포인터를 가지고 있다고 가정하고 있기 때문에 당신이 자신을 다운 캐스팅하는 이유입니다.

기본 클래스에서 포인터를 꺼내서 순수한 가상 get_engine () 함수로 바꾸십시오. 그런 다음 Foocar 및 Barcar는 포인터를 올바른 엔진 유형으로 유지할 수 있습니다.

(편집하다)

이것이 작동하는 이유 :

가상 함수 이후 Car::get_engine() a 참조 또는 포인터, C ++는 파생 클래스 가이 기능을 다른 반환 유형, 리턴 유형이보다 파생 된 유형으로 만 다릅니다.

이것은 ... 불리운다 공분산 반환 유형, 그리고 각각을 허용합니다 Car 올바른 것을 반환하려면 입력하십시오 Engine.

다른 팁

내가 추가하고 싶었던 한 가지 :이 디자인은 내가 부르는 것에 이미 나에게 나쁜 냄새가납니다. 평행 나무.

기본적으로 평행 클래스 계층 구조 (자동차와 엔진과 마찬가지로)로 끝나면 문제를 요구합니다.

엔진 (및 자동차)에 서브 클래스가 필요하거나 각각 동일한 기본 클래스의 다른 인스턴스 일지 여부를 다시 생각합니다.

또한 엔진 유형을 다음과 같이 템플릿 할 수도 있습니다

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

class FooCar : public Car<FooEngine>

class BarCar : public Car<BarEngine>

왜 자동차가 엔진으로 구성 될 수 없는지 모르겠습니다 (Barcar에 항상 Barengine이 포함 된 경우). 엔진은 자동차와 매우 강한 관계가 있습니다. 내가 선호하는:

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

Foocar가 Barengine을 사용할 수 있습니까?

그렇지 않은 경우, 올바른 엔진으로 올바른 자동차 객체를 만들기 위해 추상적 인 Factory를 사용하고 싶을 수도 있습니다.

Fooengine을 Foocar, Barengine에 Barengine에 보관할 수 있습니다.

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

이 접근법의 문제는 엔진을 얻는 것이 가상 호출 (우려하는 경우)이며 방법입니다. 환경 엔진 Car 다운 캐스팅이 필요합니다.

나는 그것이 의존한다고 생각합니다 Engine 개인적으로 만 사용됩니다 Car 그리고 그 아이들이거나 다른 물체에서 그것을 사용하고 싶다면.

만약에 Engine 기능은 구체적이지 않습니다 Cars, 나는 a를 사용할 것이다 virtual Engine* getEngine() 기본 클래스에서 포인터를 유지하는 대신 방법.

논리가 특정한 경우 CarS, 나는 공통점을 놓는 것을 선호합니다 Engine 별도의 객체 (반드시 다형성이 아님)의 데이터/로직을 유지하고 FooEngine 그리고 BarEngine 각각의 구현 Car 어린이 수업.

구현 재활용이 인터페이스 상속보다 더 필요할 때 객체 구성은 종종 유연성을 더 많이 제공합니다.

Microsoft의 COM은 일종의 어리석지 만 새로운 개념이 있습니다. 객체의 인터페이스에 대한 포인터가 있다면 쿼리하여 다른 인터페이스를 사용하여 다른 인터페이스를 지원하는지 확인할 수 있습니다. QueryInterface 기능. 아이디어는 엔진 클래스를 여러 인터페이스로 나누어 독립적으로 사용할 수 있도록하는 것입니다.

내가 뭔가를 놓치지 않았으므로, 이것은 다소 사소해야합니다.

이를 수행하는 가장 좋은 방법은 엔진에서 순수한 가상 기능을 만드는 것입니다. 그러면 인스턴스화 해야하는 파생 클래스에서 요구됩니다.

추가 신용 솔루션은 아마도 iengine이라는 인터페이스를 갖는 것일 것입니다. 대신 자동차로 전달되며 Iengine의 모든 기능은 순수한 가상입니다. 원하는 기능 중 일부 (즉, '공유')를 구현하는 '베이스 엔진'을 가질 수 있습니다.

인터페이스는 엔진처럼 '외모'를 원하지만 아마도 그렇지 않을 것입니다 (예 : 모의 테스트 클래스 등).

Foocar 객체가 다운 캐스팅없이 Fooengine의 멤버 기능을 호출 할 수 있도록 물건을 정리할 수있는 방법이 있습니까?

이와 같이:

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)
  {}
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top