2009-08-07 17 views
15

Angenommen, ich habe eine Klasse namens "Base" und eine Klasse namens "Derived", die eine Unterklasse von Base ist und auf geschützte Methoden und Member von Base zugreift.Gibt es eine Möglichkeit, die Unterklasse meiner Klasse zu verbieten?

Was ich jetzt tun möchte, ist es so zu machen, dass keine anderen Klassen Derived ableiten können. In Java kann ich das erreichen, indem ich die Abgeleitete Klasse "final" deklariere. Gibt es einen C++ Trick, der mir den gleichen Effekt geben kann?

(Idealerweise möchte ich es so machen, dass keine andere Klasse als Derived Base ableiten kann. Ich kann nicht einfach den gesamten Code in die gleiche Klasse setzen oder das Friend-Schlüsselwort verwenden, da Base und Derived sind sowohl als Templat, mit Basis weniger Vorlage Argumente, die als Abgeleitet tut ....)

+0

Ich würde einige Quellcode enthalten - da es Vorlagen beinhaltet. – sylvanaar

+0

Wenn eine Klasse keinen virtuellen Destruktor hat, sollte man davon wahrscheinlich nicht dasdieren (das ist nur eine gute Programmierung). C++ erkennt, dass Sie manchmal die Grenzen strecken müssen und Sie trotzdem davon erben können. Dein Problem ist also keine Sprache, sondern eine pädagogische. –

+0

Abstimmung: Entfernen Sie das Schlüsselwort/final-Tag, aber fügen Sie ** abgeleitete Klasse ** -Tag stattdessen – fmuecke

Antwort

12

Ab C++ 11, können Sie das Schlüsselwort final (technisch eine spezielle Kennung, da es nicht wirklich ein Schlüsselwort) in Ihrer Klasse, zB

class Derived final 
{ 
... 

Sie können mehr über das Schlüsselwort final lesen unter http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1 Interessant, diese Lösung mit C++ 11 zu haben. Ein zusätzlicher Link zu einer detaillierteren Beschreibung würde diese Antwort noch hilfreicher machen. – Wolf

9

Sie einen privaten Konstruktor für ‚Derived‘ haben kann, und eine öffentliche statische Funktion für Instanziierung erstellen

5

Der einfachste Weg, Subklassifizieren zu verbieten, indem der Konstruktor privat gemacht wird:

class Foo 
{ 
private: 
    Foo() {} 

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

Edit: Dank Indeera für den Hinweis auf, dass dies muss eine statische Factory-Methode

+0

Ah, aber wie würdest du eine Instanz von Foo erstellen? :) – Indy9000

+0

Foo.CreateFoo() –

+1

Eigentlich 'Foo :: CreateFoo()', obwohl, warum es einen Zeiger zurückgibt, ist ein Mysterium für mich, da die Klasse perfekt kopiert werden kann ... –

3

Es gibt keine einfache und saubere Art und Weise, es zu tun.

Was die Standardbibliothek tut, ist einfach, den Destruktor nicht virtuell zu machen. Das verhindert zwar nicht das Unterklassenbildung, aber es ist ein starkes Signal für die Benutzer, dass es nicht für die Vererbung ausgelegt ist, und es bedeutet, dass Sie sehr vorsichtig sein müssen, wenn Sie die abgeleitete Klasse verwenden.

Letztlich aber, tun Sie Notwendigkeit absolut Subklassifizieren unmöglich zu machen? Ist es nicht gut genug, um darauf hinzuweisen, dass "das Ableiten von dieser Klasse eine schlechte Idee ist"?

Menschen können immer Ihren Code brechen, wenn sie wirklich wollen. Das Beste, was Sie tun können, ist ihnen bewusst zu machen, was sie tun sollten und was nicht, und hoffen, dass sie nicht aktiv versuchen, um Ihren Code zu brechen.

Schützen Sie Ihren Code gegen Murphy, nicht Machiavelli. ;)

+0

Gibt es Tools, die warnen, wenn Sie dieses "starke Signal" übersehen? – Wolf

1

Da Sie Vorlagen verwenden, dachte ich, dass der letzte Teil Ihrer Frage über das Verhindern einer anderen Klasse als Abgeleitet von einer Unterklasse von Base mit geeigneten Teilspezialisierungen durchgeführt werden könnte.

Das folgende Code-Snippet ist, was ich kam, aber die erforderliche Komplexität geht nur, um die Antwort von jalf zu verstärken. Ist es das wert? Wenn überhaupt, dann hat das mir geholfen, eine teilweise Spezialisierung besser zu verstehen, als eine Technik auszuarbeiten, die ich jemals in der Praxis anwenden würde.

Ich benutze COMMON, um einen gemeinsamen Template-Parameter zwischen Base und Derived und EXTRA anzugeben, um die zusätzlichen Parameter zu bezeichnen, die Sie sagen, Abgeleitet hat. Die tatsächliche Anzahl von diesen könnte alles sein, was ich zufällig ausgewählt habe, jeweils eins und zwei für diese jeweils.

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

Dieser Code wurde mit gcc 4.3.2 getestet.

Beachten Sie, dass eine Alternative zur Friend-Deklaration darin besteht, den Konstruktor in der partiellen Spezialisierung von Base zu schützen, aber dies würde Klassen wie DishonestDerived erlauben zu arbeiten.

Verwandte Themen