2011-03-07 5 views
2

나는 "두려운"다이아몬드 상속 문제를 겪어야하는 경우를 발견 한 것 같습니다. 그러나 코드는 정상적으로 작동하는 것으로 보입니다. 내가 확실히 알아낼 수없는 것은 수 있다면 문제입니다.제 3 자 라이브러리를 사용하는 다이아몬드 상속 문제

다음은 설정입니다. MFC를 사용하고 마우스 클릭 윈도우 메시지의 커스텀 핸들링을 추가하기 위해 CEdit를 확장했습니다. 그런 다음이 클래스와 타사 개발자가 작성한 클래스 (이 예제에서는 Bob이라고 함)를 상속받습니다. 이렇게하면 이제 특수 컨트롤이나 향상된 컨트롤의 버전을 반환 할 수 있습니다. 문제는 Bob의 라이브러리를 수정할 수 없으며 두 코드가 모두 CEdit (및 해당 문제에 대해서는 CWnd)에서 상속된다는 것입니다.

예제 코드 :

class A : public CEdit {...}  // From Bob's library 
class B : public A {...}   // From Bob's library 
class BobsEdit : public B {...} // From Bob's library 

// My version which handles WM_LBUTTONDOWN, WM_CREATE 
// and does a couple other cool things. 
class MyEdit : public CEdit 
{ 
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct) 
    { 
     if (!CEdit::Create(...)) return -1; 

     ...set some style stuff... 
    } 

    afx_msg void OnLButtonDown(UINT nFlags,CPoint point) {} // Override CWnd handler 
} 

class MyBobsEdit : public BobsEdit, public MyEdit {} // My version of Bob's control 


// CBobsUser returns just a standard CEdit and BobsEdit control 
// This is also from Bob's library. 

class CBobsUser 
{ 
    CWnd* GetMeAnEditBox() 
    { 
     CEdit* pEdit; 
     if (...some condition...) 
      pEdit = new CEdit(); 
     else 
      pEdit = new BobsEdit(); 

     ... 
     return pEdit; 
    } 
} 

// CMyUser overrides Bob's GetMeAnEditBox and returns 
// one of my custom controls (with the new cool handler). 
class CMyUser : public CBobsUser 
{  
... 

    CWnd* GetMeAnEditBox() 
    { 
     MyEdit* pEdit; 
     if (...some condition...) 
      pEdit = new MyEdit(); 
     else 
      pEdit = new MyBobsEdit(); 

     ... 
     return pEdit; 
    }     
} 

그래서 ... 질문은 다음과 같습니다

  1. 왜이 다이아몬드 상속 문제에서 고통을하지 않는 것?
  2. 미래에 나를 물릴 수있는이 디자인에 표시되지 않는 문제가 있습니까?
  3. 이 나는 ​​다이아몬드의 한면에 코드를 변경할 수없는 경우이 문제를 해결하는 또 다른 방법입니다 (즉, 둘 다 측면 가상의 CEdit를 선언 할 수 없습니다?)

감사합니다!

답변

3

광고 1 : 개체가 CBobsEdit인지 아무 것도 모르기 때문에. 개체를 MyBobsEdit으로 만들지 만 즉시 MyEdit으로 캐스팅하므로 모든 메서드 호출은 MyEdit이고 불규칙한 호출 오류가 발생하지 않으며 캐스트 자체도 모호하지 않습니다. CBobsEdit의 기능이 사용되지 않습니다 (하위 클래스에는 메소드가 없습니다). 생성되지만 부모에게 추가되지 않으므로 결코 표시되지 않고 사용되지 않습니다.

광고 2 : 글쎄, BobsEdit을 사용하고 있지 않습니다. 내가 생각하기에, 당신이 원하는 것이 아닙니다.

광고 3 : 당신은 MyEdit이 템플릿 인수의에서 상속 템플릿을 만들고 CEdit에서 하나의 경우와 다른 경우 CBobsEdit에서 직접 상속 할 수 있습니다. 이 기술은 종종 "mixin"이라고합니다. 마찬가지로 :

template <typename BaseEditT> 
class MyEdit : public BaseEditT { ... } 

불행하게도 MyEdit<CEdit>MyEdit<CBobsEdit>는 관련이없는 클래스이다. 포인터를 CEdit (항상 기본 클래스 임)으로 저장할 수 있다면 인터페이스를 정의하고 MyEdit에서이 인터페이스를 구현하고 해당 인터페이스에 대한 포인터를 저장해야합니다. 인터페이스에 캐스트 연산자가 CEdit& (및 CEdit const&)이어야하며이 인터페이스에 CEdit 메서드를 호출 할 수 있어야합니다. 이처럼 :

class IMyEdit { 
    virtual operator CEdit &() = 0; 
    virtual operator CEdit const &() const = 0; 
}; 

template <typename BaseEditT> 
class MyEdit : public BaseEditT { 
    operator CEdit &() { return *this; } 
    operator CEdit const &() const { return *this; } 
}; 

주, 당신이 별도의 파일에 넣어 당신이 피하기 위해 CMyUser 생성자를 정의하는 경우에만 포함시킬 수 있도록 MyEdit 템플릿의 정의를 참조 할 필요가 개체를 구성하는 코드 만 이상 컴파일 타임에 벌금.

+0

가상 함수 나 데이터 멤버를 구현 한 파생 클래스가없는 경우에는 그 사이에 willy-nilly를 캐스팅 할 수 있습니다. 일반적으로 이것은 C++ 커뮤니티가 문서화되지 않은 컴파일러 관련 작업에 의존하므로 frowned하게 될 것입니다. 그러나 MFC는이 작업에 크게 의존하므로 사용자는 안전해야합니다. –

관련 문제