2009-08-07 14 views
15

Dire che ho una classe chiamata "Base" e una classe chiamata "Derivata" che è una sottoclasse di Base e accede a metodi protetti e membri di Base.C'è un modo per proibire la sottoclasse della mia classe?

Quello che voglio fare ora è fare in modo che nessun'altra classe possa creare una sottoclasse derivata. In Java posso realizzarlo dichiarando la classe derivata "final". C'è qualche trucco in C++ che può darmi lo stesso effetto?

(Idealmente mi piacerebbe fare in modo che nessuna classe diversa da Derived possa sottoclasse Base. Non posso semplicemente mettere tutto il codice nella stessa classe o usare la parola chiave friend, poiché Base e Derived sono sia su modelli, con la base avendo un minor numero di argomenti di template di Derived fa ....)

+0

Includerei del codice sorgente, poiché riguarda i modelli. – sylvanaar

+0

Se una classe non ha un distruttore virtuale, probabilmente non ne dovresti dedurre (è solo una buona programmazione). C++ riconosce che a volte hai bisogno della capacità di allungare i limiti e ti consente comunque di ereditarlo. Quindi il tuo problema non è una lingua, ma una questione educativa. –

+0

Voto: rimuovi la parola chiave/tag finale ma aggiungi ** tag di classe derivata ** invece – fmuecke

risposta

12

A partire dal C++ 11, è possibile aggiungere la parola finale (tecnicamente un identificatore speciale dal momento che non è in realtà una parola chiave) per la classe, per esempio

class Derived final 
{ 
... 

Si può leggere di più la parola finale a http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1 Interessante avere questa soluzione con C++ 11. Un ulteriore collegamento a una descrizione più dettagliata renderebbe questa risposta ancora più utile. – Wolf

9

si può avere un costruttore privato per 'Derived' ed una public static Crea funzione per la creazione di un'istanza

5

Il modo più semplice per vietare la creazione di sottoclassi è rendendo privato il costruttore:

class Foo 
{ 
private: 
    Foo() {} 

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

Edit: Grazie a Indeera per aver ricordato che questo ha bisogno di un metodo di fabbrica statica

+0

Ah, ma come creeresti un'istanza di Foo? :) – Indy9000

+0

Foo.CreateFoo() –

+1

In realtà 'Foo :: CreateFoo()', anche se il motivo per cui restituisce un puntatore è un mistero per me, dal momento che la classe può essere perfettamente copiata ... –

3

Non c'è un modo semplice e pulito per farlo.

Quello che fa la libreria standard è semplicemente rendere il distruttore non virtuale. Ciò non impedisce la sottoclasse, ma è un segnale forte per gli utenti che non è progettato per l'ereditarietà, e significa che devi stare molto attento quando usi la classe derivata.

In ultima analisi, però, si fa necessità per essere assolutamente sottoclassi impossibile? Non è sufficiente indicare che "derivare da questa classe è una cattiva idea"?

Le persone possono sempre violare il codice se lo desiderano. Il meglio che puoi fare è renderli consapevoli di ciò che dovrebbero o non dovrebbero fare, e spero che non lo facciano attivamente. prova per violare il tuo codice.

Proteggi il tuo codice contro Murphy, non Machiavelli. ;)

+0

Ci sono strumenti che avvisano se si trascura questo "segnale forte". – Wolf

1

Poiché si utilizzano i modelli, pensavo che l'ultima parte della domanda relativa alla prevenzione di una classe diversa da Derivata alla sottoclasse da Base potrebbe essere eseguita utilizzando specializzazioni parziali appropriate.

Il seguente frammento di codice è quello che ho trovato, ma la complessità richiesta serve solo a rafforzare la risposta di jalf. Ne vale la pena? Semmai questo mi ha aiutato a capire una specializzazione parziale piuttosto che elaborare una tecnica che avrei mai usato nella pratica.

Io uso COMMON per indicare un parametro di modello condiviso tra Base e Derivato e EXTRA per indicare i parametri aggiuntivi che hai dichiarato Derivato. Il numero effettivo di questi potrebbe essere qualsiasi cosa che mi è capitato di aver scelto rispettivamente uno e due per questi.

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

Questo codice è stato testato con gcc 4.3.2.

Si noti che un'alternativa alla dichiarazione di amicizia sarebbe quella di proteggere il costruttore nella specializzazione parziale di Base, ma in tal caso consentirebbe a classi come DishonestDerived di funzionare.

Problemi correlati