2009-08-07 16 views
15

Powiedzmy, że mam klasę o nazwie "Base" i klasę o nazwie "Derived", która jest podklasą Base i uzyskuje dostęp do metod chronionych i członków Base.Czy istnieje sposób, aby zabronić podklasy mojej klasy?

To, co chcę teraz zrobić, to sprawić, aby żadne inne klasy nie mogły uzyskać podklasy Derived. W Javie mogę to osiągnąć, deklarując klasę pochodną "final". Czy istnieje sztuczka C++, która może dać mi taki sam efekt?

(Idealnie chciałbym uczynić tak, aby żadna klasa inna niż pochodna Derived nie mogła również podlać bazy.Nie mogę po prostu umieścić cały kod w tej samej klasie lub użyć słowa kluczowego przyjaciel, ponieważ Podstawowy i Pochodny są zarówno na matrycy, a podstawa ma mniej argumentów szablonu niż Pochodzące robi ....)

+0

Chciałbym dołączyć kod źródłowy - ponieważ dotyczy szablonów. – sylvanaar

+0

Jeśli dana klasa nie ma wirtualnego destruktora, prawdopodobnie nie powinna to być dertiving (to tylko dobre programowanie). C++ zdaje sobie sprawę, że czasami potrzebna jest możliwość rozciągnięcia granic i pozwala w ten sposób dziedziczyć. Tak więc problem nie jest językiem, który stawia się edukacyjnym. –

+0

Głosuj: usuń słowo kluczowe/ostateczny tag, ale dodaj ** tag klasy pochodnej ** zamiast: – fmuecke

Odpowiedz

12

Począwszy od C++ 11, można dodać ostatecznego słowa kluczowego (technicznie specjalny identyfikator, ponieważ nie jest w rzeczywistości kluczowe) do swojej klasy, np

class Derived final 
{ 
... 

można przeczytać więcej na temat ostatecznego hasła pod adresem http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1 Ciekawe, że ma to rozwiązanie w C++ 11. Dodatkowy link do bardziej szczegółowego opisu sprawiłby, że ta odpowiedź byłaby jeszcze bardziej pomocna. – Wolf

9

może mieć prywatny konstruktor „Derived” i public static Utwórz funkcję instancji

5

najłatwiej jest zakazanie podklasy przez uczynienie konstruktora prywatnym:

class Foo 
{ 
private: 
    Foo() {} 

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

Edit: Dzięki Indeera za wskazanie, że to musi statycznej metody fabryki

+0

Ah, ale w jaki sposób stworzysz instancję Foo? :) – Indy9000

+0

Foo.CreateFoo() –

+1

Właściwie 'Foo :: CreateFoo()', ale dlaczego zwraca wskaźnik jest dla mnie zagadką, ponieważ klasa może być idealnie skopiowana ... –

3

Nie ma prosty i czysty sposób to zrobić.

To, co robi standardowa biblioteka, polega na tym, aby destruktor był niewirtualny. To nie zapobiega podklasowaniu, ale jest silnym sygnałem dla użytkowników, że nie jest przeznaczony do dziedziczenia, a to oznacza, że ​​musisz być bardzo ostrożny przy używaniu klasy pochodnej.

Ostatecznie, czy potrzebujesz potrzebujesz, aby całkowicie uniemożliwić podklasowanie niemożliwe? Czy to nie wystarczy wskazać, że "wywodzenie z tej klasy jest złym pomysłem"?

Ludzie zawsze mogą złamać twój kod, jeśli naprawdę tego chcą. Najlepsze, co możesz zrobić, to uświadomić im, co powinni, a czego nie powinni robić, i mieć nadzieję, że nie będą aktywnie próbować spróbuj, aby złamać kod.

Chroń swój kod przed Murphy, a nie Machiavelli. ;)

+0

Czy są narzędzia, które ostrzegają, jeśli przeoczysz ten "silny sygnał". – Wolf

1

Ponieważ używasz szablonów, myślałem, że ostatnia część twojego pytania o zapobieganie jakiejkolwiek klasie innej niż pochodna Subclass od Base może być wykonana przy użyciu odpowiednich częściowych specjalizacji.

Poniższy fragment kodu jest tym, co wymyśliłem, ale złożoność wymagana tylko po to, by wzmocnić odpowiedź jalf. Czy warto? Jeśli cokolwiek to pomogło mi zrozumieć częściową specjalizację, bardziej niż wypracowanie techniki, którą bym kiedykolwiek użył w praktyce.

Używam COMMON, aby wskazać współużytkowany parametr szablonu między Base i Derived i EXTRA, aby wskazać dodatkowe parametry, które wypowiada Derived. Rzeczywista liczba tych może być czymkolwiek, co akurat wyszukałem odpowiednio dla nich jednego i dwóch.

// 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; 

Ten kod został przetestowany pod kontrolą gcc 4.3.2.

Należy zauważyć, że alternatywą dla deklaracji przyjaciela byłoby uczynienie konstruktora chronionym w częściowej specjalizacji Bazy, ale wtedy pozwoliłoby to na działanie klas takich jak DishonestDerived.

Powiązane problemy