2013-03-01 6 views
1

내가 관련 세터/게터와 그 서브 클래스는 C++ (11) enum class 속성이 것 같은 클래스를 구축하기 위해 노력하고있어과 코드 재사용을 달성하기 위해 너무 좋아!어떻게 중첩 열거

// Base as before 

class Derived : public Base { 
    public: 

    enum class MyEnum { 
     C, 
     D 
    }; // intention: to override Base's "my_enum" attribute 

    Derived(): Base() { 
     my_enum = MyEnum::C; 
     // ERROR: cannot convert 'Derived::MyEnum' to 'Base::MyEnum' 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    Derived Deriv; 

    // ERROR: no match for 'operator==' for types 'Base::MyEnum' and 'Derived::MyEnum' 
    Deriv.get() == Derived::MyEnum::C; 

    return 0; 
} 

내가 뭘 이해 : 모든 시간을/세터/게터를 다시 구현 속성을 가지고 있지 동안

그러나, 나는 파생 좋아할 클래스는 MyEnum 열거 클래스를 다시 정의 할 수 문제는; 이 경우 코드를 재사용 할 수있는 가장 깨끗한 방법을 찾고 있습니다.

바람직하게는 상속을 통해서만 (또는 오히려 Base()에서 파생 된 행위로 인해 Derived() 클래스에서 기능을 사용할 수 있어야합니다).

제안 사항?

+1

이것은 의문의 여지가 있습니다. 열거 형을 기준으로 전환 하시겠습니까? 'enums'를 클래스로 대체하면 이중 상속 계층이 생깁니다. 그것이 속한 곳에서 행동을 수업 시간에 두십시오. –

+0

@PeterWood : 다시 말해서, "그냥 그걸로 살아 가세요"(그리고 똑같은 setter/getter/속성 이름을 1 개의'enum'을 위해서가 아니라 수십 개의 파생 클래스에 복사하여 붙여 넣기)합니까? –

+0

아니. 내가 말한 것을 어떻게 얻었는지 모르겠다. 나는 세터와 게터들을 피하려고 노력한다. 이것은'Strategy' 패턴을 사용하는 좋은 사례 인 것처럼 보입니다. 객체의 수명 동안 값이 바뀌면 'State' 패턴을, 그렇지 않으면'Visitor' 패턴을 사용합니다. –

답변

1

Base 템플릿을 열거 형으로 매개 변수화하고 특성 클래스를 사용하여 파생 형식별로 특수화 할 수있는 "기본"열거 형을 제공 할 수 있습니다.

template<typename T> 
struct MyEnumTraits 
{ 
    enum class type { 
     A, 
     B 
    }; 

    static const type default_value = type::A; 
}; 

template<typename T = void> 
class Base { 
    public: 

    typedef typename MyEnumTraits<T>::type MyEnum; 
    MyEnum my_enum; 

    Base() : my_enum(MyEnumTraits<T>::default_value) {} 

    MyEnum get() { return my_enum; } 
    void set(const MyEnum &ME) { my_enum = ME; } 
}; 

class Derived; 

template<> 
struct MyEnumTraits<Derived> 
{ 
    enum class type { 
     C, 
     D 
    }; 
    static const type default_value = type::C; 
}; 

class Derived : public Base<Derived> { 
    // ... 

그러나 파생 된 유형마다 다른 기본 클래스가 생기며, 이는 아마도 원하는 것이 아닙니다. 비 템플릿 Base을 유지하고 getter 및 setter를 Base에서 파생 된 중간 클래스 템플리트로 이동시킨 다음 파생 된 유형을 그로부터 파생시킬 수 있습니다.

class Base { ... }; 

template<typename T = void> class GetterSetterImpl : public Base { ... }; 

class Derived : public GetterSetterImpl<Derived> { ... }; 
+0

음 ... 흥미 롭다! 그러나'Derived '클래스의'enum'을 재정의하려면 템플릿 전문화가 필요합니다, 그렇죠? 약간 어색한 것 같아 .. –

+0

서투른 쪽이 눈앞에있다.) 기본 클래스에서 참조하기를 원한다면'Derived' 안에 열거 형을 정의 할 수 없다.'Derived' 유형은 당신은'GetterSetterImpl ' –

1

컴파일러가 맞습니다. 열거 형 Base::MyEnumDerived::MyEnum이 상속을 통해 연결된 클래스에 정의되어 있지만 열거 자체는 관련이없는 것으로 간주됩니다. 그들은 단지 의 정규화되지 않은이라는 이름을 가지기 때문에 컴파일러에는 아무런 의미가 없습니다. 컴파일러에 관한 한 두 가지 열거 형은 서로 관련이 없습니다.

열거 형이 두드러진 방식으로 구현되는 방식을 생각해 보면 이해할 수 있습니다. 강력한 형식 임에도 불구하고 열거 형은 작은 정수로 유지됩니다. 두 개는 서로 관련이 없으므로 Base::MyEnum::ADerived::MyEnum::C과 동일한 값을 가지며 런타임에 두 값을 구분할 수있는 것이 없습니다.

기본 클래스의 enum에 모든 열거 값을 덤프하는 것 외에도 (사용자 자신의 라이브러리 외부로 확장 할 수있는 기회를 없애는) C++ enumerations do not support inheritance은 일반적으로 매우 유연하지 않습니다.

+0

그래서 기본적으로 당신은 "단지 그것과 함께"(=='enum'의 또 다른 정의를 필요로하는 모든'Derived' 클래스에서'get/set' 보일러 판을 모두 복사하십시오)? –

+1

@RodyOldenhuis 더 나은 점은 확장 성이 필요한 'enums'(즉 "다른 정의가 필요함")를 피하기 위해 디자인을 변경하는 것이 좋습니다. 하위 클래스가 다른 열거 형을 사용하면 다른 getter와 다른 setter가 있어야한다는 것이 당연합니다. 결국 완전히 다른 유형의 속성에 대해 새로운 getter/setter 세트를 작성하게됩니다 (예 :'double' 대신 'int')? 'enum class'는 built-in 타입과 똑같은 강하게 타입 화 된 특징을 가지고 있으며 마찬가지로 상속도 제공하지 않습니다. 물론 템플릿 경로는 언제든지 갈 수 있지만 디자인의 선명도가 떨어질 수 있습니다. – dasblinkenlight

1

이것은 나쁜 아이디어이며 작동하지 않을 것입니다.

  • 언어 수준 C++에서는 내용을 재정의 할 수 없습니다. 같은 이름을 가진 새롭고 관련없는 것들을 숨길 수 있습니다 (완전히 아님). 가상 함수를 서명을 유지하면서 새 구현을 제공하여 재정의 할 수도 있습니다.
  • 디자인 수준 기본 클래스는 파생 클래스가 준수해야하는 계약을 정의합니다. Base::get()MyEnum {A, B}을 반환하면 모든 파생 클래스는 MyEnum {A, B}을 반환하는 get()을 가져야합니다. 이것이 상속이 전부입니다 (코드 재사용이 아니라 코드 재사용이 아니라).

상속에 의존하지 않고 클래스를 템플릿으로 만들어 코드 재사용을 달성 할 수도 있습니다.

+0

코드 재사용을 위해 상속을 사용하는 것은 나쁜 생각이라는 것을 알고 있습니다. 물론 IRL의'Base()'와'Derived()'는 훨씬 더 복잡하지만 원칙적으로 상속을 정당화하는 기본적인 'is-a'관계를 따른다. 나는 반복적 인 코드의 일부를 제거하기 위해 기존의 메커니즘/인터페이스를 사용할 수 있는지 궁금하다. 따라서, "상속을 통해서만"의 요구 사항을 완화 할 때 : 템플릿/컴포지션/무엇을 사용하여 어떻게 할 수 있습니까? –

+0

@RodyOldenhuis : enum "is-a"는 클래스 상속의 ** 반대 방향으로 ** 이동합니다. 기본 클래스 열거 형의 값은 파생 클래스 열거 형의 하위 집합이며 파생 클래스 인스턴스는 기본 클래스 인스턴스의 하위 집합입니다. 이것은 모든 정적 유형을 존중하면서 객체 유형에 유효하지 않은 값을 설정하는 것을 가능하게합니다. 따라서 디자인 수준의 오류가 발생했습니다. 생각했다. –

+0

@ Cheersandhth.-Alf : 잘 모르겠다. 'Base'와'Derived'의 열거 형은 완전히 관련이 없으므로 다른 것의 서브셋/수퍼 세트도 아닙니다. 또한,'Derived' 클래스는'Base'의 * super * 집합입니다. (그들은'Base' *와 *에서 모든 것을 얻습니다.) 그래서 ... 조금 더 자세히 설명해 주시겠습니까? –