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.
Includerei del codice sorgente, poiché riguarda i modelli. – sylvanaar
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. –
Voto: rimuovi la parola chiave/tag finale ma aggiungi ** tag di classe derivata ** invece – fmuecke