2011-10-28 6 views
10

클래스 계층 구조의 디자인에서 파생 클래스가 구현할 다양한 메서드를 선언하는 추상 기본 클래스를 사용하고 있습니다. 어떤 점에서, 기본 클래스는 C++에서 얻을 수있는 인터페이스에 가깝습니다. 그러나 특정 문제가 있습니다. 당신이 C에서 추상 클래스를 반환 할 수 없기 때문에 ++, 물론기본 클래스의 추상 형식 반환

class Interface { 
public: 
    virtual Interface method() = 0; 
}; 

class Implementation : public Interface { 
public: 
    virtual Implementation method() { /* ... */ } 
}; 

는,이 컴파일되지 것입니다 : 우리의 인터페이스 클래스를 선언하는 아래의 코드를 생각해 보자. 나는 다음과 같은 솔루션을 사용하고이 문제를 해결하기 위해서는 :

template <class T> 
class Interface { 
public: 
    virtual T method() = 0; 
}; 

class Implementation : public Interface<Implementation> { 
public: 
    virtual Implementation method() { /* ... */ } 
}; 

이 솔루션의 작품을 나에게, 그러나, 벌금과 멋쟁이 모두, 그것 때문에 텍스트의 중복 비트의 매우 우아한 보이지 않는 인터페이스에 대한 매개 변수가됩니다. 여러분이이 디자인에 대한 다른 기술적 인 문제를 지적 할 수 있다면 행복 할 것입니다.하지만이 점만이 내 유일한 관심사입니다.

중복 템플릿 매개 변수를 제거 할 방법이 있습니까? 아마도 매크로를 사용하고 있습니까?

참고 : 해당 메서드는 인스턴스를 반환해야합니다. 나는 method()이 포인터 나 참조를 반환했다면 아무런 문제가 없다는 것을 알고 있습니다.

+1

귀찮게 꾸미는 관용어는 [Curiously Recurring Template Pattern] (http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Curiously_Recurring_Template_Pattern)이라고합니다.나는 당신이'#define DERIVE_TEMPLATE_BASE (Derived, Base) 클래스 Derived : Public Base '과 같은 매크로로 클래스 선언을 대체 할 수있을 것이라고 생각하지만, 매우 추해 보일 뿐더러 편집기를 혼란스럽게 할 것입니다. 최종선 - 그렇습니다, 중복이 있습니다, 그러나 잘 설립되고 인식 할 수있는 관용구입니다. – gwiazdorrr

+0

@ gwiazdorrr : 그렇게 나쁘지는 않습니다. 그것이 인식 된 관용어라면, 나는 단지 그것이 "인터페이스"의 사용자들에게 익숙하지 않을 것이라고 가정 할 수 있습니다, 맞습니까? – Zeenobit

+1

CRTP는 가장 파생 된 유형이 기본 유형을 사용하는 것으로 알려져 있기 때문에 호출을 가상으로 만드는 것은 중요하지 않습니다 (이 예제에서는 적어도). –

답변

5

Interface::method() 포인터 또는 참조를 사용하지 않고 Interface 인스턴스를 반환 할 수 없습니다. 포인터가 아닌 포인터, 참조가 아닌 Interface 인스턴스를 반환하면 Interface이 추상이므로 인스턴스가 Interface 인 자체가 불법입니다.

포인터 :

class Interface 
{ 
public: 
    virtual Interface* method() = 0; 
}; 

class Implementation : public Interface 
{ 
public: 
    virtual Interface* method() { /* ... */ } 
}; 

참조 :

class Interface 
{ 
public: 
    virtual Interface& method() = 0; 
}; 

class Implementation : public Interface 
{ 
public: 
    virtual Interface& method() { /* ... */ } 
}; 

템플릿 매개 변수 :

당신이 기본 클래스는 객체의 인스턴스를 반환하려면 다음 중 하나를 사용해야합니다
template<type T> 
class Interface 
{ 
public: 
    virtual T method() = 0; 
}; 

class Implementation : public Interface<Implementation> 
{ 
public: 
    virtual Implementation method() { /* ... */ } 
}; 
5

명백한 r에 대해 으로 반환 할 수는 없지만 easons, 그것은 포인터 또는 참조를 반환 완전히 OK이다 -이라고 "공변 반환 형식"을, 그리고 가상 함수 오버라이드 (override)의 유효한 형태 :

struct Base { virtual Base * foo(); } 
struct Derived : Base { virtual Derived * foo(); } 

요점은 Derived::foo() 정품 재정 때문이다 , 그리고 기본 숨김 오버로드가 아니기 때문에 Derived*Base의 파생 클래스에 대한 포인터입니다. 참고 문헌에도 동일하게 적용됩니다. 즉

, 당신은 Base * p 있고, 당신이 p->foo()를 호출하는 경우, 당신은 항상 Base에 대한 포인터로 결과를 처리 (수 있지만, 당신은 당신의 클래스는 다음 사실 Derived 당신입니다 같은 추가 정보를 가지고있는 경우 그 정보를 사용할 수 있습니다).

"contravariant argument types"과 반대되는 구성 순서는 C++의 일부로 허용되지 않습니다.

관련 문제