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.
Chciałbym dołączyć kod źródłowy - ponieważ dotyczy szablonów. – sylvanaar
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. –
Głosuj: usuń słowo kluczowe/ostateczny tag, ale dodaj ** tag klasy pochodnej ** zamiast: – fmuecke