2009-08-07 4 views
15

"Base"라는 클래스가 있고 Base의 하위 클래스 인 "Derived"라는 클래스가 있으며 Protected 메서드와 Base 멤버에 액세스한다고 가정 해 보겠습니다.내 클래스의 서브 클래 싱을 금지 할 수있는 방법이 있습니까?

지금하고 싶은 것은 다른 클래스가 파생 클래스로 하위 클래스를 만들 수 없도록 만드는 것입니다. 자바에서는 Derived 클래스를 "final"이라고 선언하여이를 수행 할 수 있습니다. 저에게 동일한 효과를 줄 수있는 C++ 트릭이 있습니까?

(이상적으로 Derived 이외의 다른 클래스도 Base 클래스를 하위 클래스로 만들 수 없도록 만들고 싶습니다. Base와 Derived는 모두 동일한 클래스에 넣거나 friend 키워드를 사용할 수 없습니다. 모두

+0

템플릿이 포함되어 있으므로 일부 소스 코드가 포함됩니다. – sylvanaar

+0

클래스에 가상 소멸자가 없다면 아마도 좋은 프로그래밍이라고 생각하지 않아야합니다. C++은 가끔 경계를 확장하는 기능이 필요하고 어쨌든 상속을 허용 할 수 있음을 알고 있습니다. 그래서 문제는 언어가 교육적으로 문제가되지 않는다는 것입니다. –

+0

투표 : 키워드/최종 태그를 제거하고 대신 ** 유도 클래스 ** 태그를 추가하십시오 – fmuecke

답변

12

C++ (11), 당신은 예를 들어

class Derived final 
{ 
... 

당신은 마지막 키워드에 대한 자세한 내용을보실 수 있습니다 최종 키워드 클래스에 (이 키워드가 사실이 아니기 때문에 기술적으로 특별한 식별자)를 추가 할 수 있습니다 http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1이 솔루션을 C++로 사용하는 데 흥미 롭습니다. 11. 보다 자세한 설명에 대한 추가 링크는이 답변을 더욱 유용하게 만듭니다. – Wolf

9

당신은 'Derived'에 대한 개인 생성자와 공공 정적 인스턴스에 대한 기능을 만들기를 할 수 있습니다) .... 자료가하는 파생보다 적은 템플릿 인수를 갖는, 템플릿

5

서브 클래 싱을 금지하는 가장 쉬운 방법은 생성자를 비공개로 설정함으로써 :

class Foo 
{ 
private: 
    Foo() {} 

public: 
    static Foo* CreateFoo() { return new Foo; } 
}; 

편집 :이 정적 팩토리 메소드

+0

아,하지만 어떻게 Foo의 인스턴스를 만들겠습니까? :) – Indy9000

+0

Foo.CreateFoo() –

+1

사실,'Foo :: CreateFoo()'가 포인터를 반환하는 이유는 클래스가 완벽하게 복사 될 수 있기 때문에 나에게 수수께끼가된다. –

3

그것을 할 간단하고 깨끗한 방법은 없습니다 필요하다고 지적 Indeera 감사합니다.

표준 라이브러리는 소멸자를 비 가상으로 만듭니다. 그건 하위 클래스 화를 막지는 못하지만, 상속을 위해 설계되지 않았 음을 사용자에게 알려주는 신호이므로 파생 클래스를 사용할 때 매우 신중해야합니다. 궁극적으로하지만

, 당신을 절대적으로 서브 클래스 불가능을 할 필요를? "이 수업에서 파생되는 것이 나쁜 생각"이라는 것을 나타내는 것은 충분하지 않습니까?

사람들은 정말로 원하는 경우 항상 코드를 어길 수 있습니다. 당신이 할 수있는 최선의 방법은 그들이해야 할 것과해야 말아야 할 것을 알게하고, 그들이 적극적으로하지 않기를 바랄 것입니다. 을 시도해 코드를 해독하십시오.

마키아 벨리가 아니라 머피를 상대로 코드를 보호하십시오. ;)

+0

"강력한 신호"를 간과 할 경우 경고 도구가 있습니까? – Wolf

1

템플릿을 사용하고 있기 때문에 파생되지 않은 클래스를 기본에서 하위 클래스로 만들지 못하게하는 것에 대한 질문의 마지막 부분은 적절한 부분 특수화를 사용하여 수행 할 수 있다고 생각했습니다.

다음 코드 스 니펫은 내가 생각해 냈지만 복잡성은 단지 jalf가 답을 강화하는 데에만 필요합니다. 그만한 가치가 있니? 이것이 도움이된다면 부분적 전문화를 이해하는 데 도움이 될 것입니다.

저는 COMMON을 사용하여 Base와 Derived 사이의 공유 템플릿 매개 변수를 나타내고 EXTRA를 사용하여 파생 된 추가 매개 변수를 나타냅니다. 이것들의 실제 숫자는 방금 내가 각각 이것들에 대해 1과 2를 고른 것이 될 수 있습니다.

// Forward declaration of class Derived 
template< class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Derived; 


// Definition of general class template Base 
template< class SUBCLASS 
     , class COMMON > 
class Base 
{ 
private: 
    Base() {} 
}; 


// Definition of partial specialisation of template class Base to open up 
// access to the constructor through friend declaration. 
template< class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Base< Derived< COMMON, EXTRA1, EXTRA2 > 
      , COMMON > 
{ 
private: 
    Base() {} 

    friend class Derived< COMMON, EXTRA1, EXTRA2 >; 
}; 


// Definition of class Derived 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Derived 
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    static Derived* create() { return new Derived; } 

private: 
    Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 > 
        , COMMON >() 
    { 
    } 
}; 


// Definition of class HonestDerived. 
// It supplies itself as the SUBCLASS parameter to Base. 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class HonestDerived 
    : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 > 
          , COMMON >() 
    { 
    } 
}; 


// Definition of class DishonestDerived 
// It supplies Derived rather than itself as the SUBCLASS parameter to Base. 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class DishonestDerived 
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 > 
          , COMMON >() 
    { 
    } 
}; 


template< class COMMON, class EXTRA1, class EXTRA2 > 
class DerivedFromDerived 
    : public Derived< COMMON, EXTRA1, EXTRA2 > 
{ 
public: 
    DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >() 
    { 
    } 
}; 

// Test partial specialisation gives Derived access to the Base constructor 
Derived< int, float, double >* derived 
    = Derived< int, float, double >::create(); 

// Test that there is no access to the Base constructor for an honest subclass 
// i.e. this gives a compiler error 
HonestDerived< int, float, double > honestDerived; 

// Test that there is no access to the Base constructor for a dishonest subclass 
// i.e. this gives a compiler error 
DishonestDerived< int, float, double > dishonestDerived; 

// Test that there is no access to the Derived constructor 
// i.e. this gives a compiler error 
DerivedFromDerived< int, float, double > derivedFromDerived; 

이 코드는 gcc 4.3.2에서 테스트되었습니다.

친구 선언에 대한 대안은 Base의 부분 특수화에서 보호 된 생성자를 만드는 것이지만 DishonestDerived와 같은 클래스가 작동하도록하는 것입니다.