2015-05-04 6 views
17

C++ 11 ha introdotto la parola chiave final per vietare future sostituzioni o per proibire l'ereditarietà. L'esempio più comune in cui è possibile utilizzarlo è nel caso di classi che non sono destinate ad essere utilizzate come classi di base (sono ad esempio distruttori non virtuali). Tuttavia, a volte potremmo desiderare di avere una relazione implementata nella relazione tra due classi (ovvero l'ereditarietà private) e non una relazione - (public). Tuttavia, final proibisce entrambi i tipi di ereditarietà.Come proibire l'ereditarietà pubblica ma consentire l'ereditarietà privata (e protetta)

La mia domanda è la seguente: esiste un modo per consentire l'ereditarietà private ma proibire l'ereditarietà public (probabilmente non direttamente, ma almeno possiamo "simularlo")? In questo caso, non ci saranno problemi anche se usiamo una classe con un distruttore non virtuale, dato che non possiamo usare direttamente la classe derivata tramite un puntatore alla base, quindi dovremmo stare bene.

Sto pensando a un codice come questo:

class Base /*final*/ {}; // making it final prohibits both private and public inheritance 

class PrivateDerived: private Base{}; // this should work 

class PublicDerived: public Base{}; // this shouldn't 

int main() 
{ 
    PrivateDerived prvd; 
    PublicDerived pubd; // this should not compile 

    // Base* pBase = new PrivateDerived; // doesn't work, so we are ok 
} 
+0

protetta di default (altri) costruttori. –

+0

@ DieterLücking O distruttori, ma potrebbe risultare più difficile. –

+2

@ DieterLücking anche se rendo protetto il ctor in 'Base', la riga' class PublicDerived: public Base {}; 'compila ancora, poiché le classi derivate" sanno "come costruire la base. Di fatto, l'intero programma viene compilato. E anche il problematico 'Base * pBase = new PrivateDerived;' sarà ancora ok. – vsoftco

risposta

4

Interessante domanda! Se non ti dispiace rinunciare banalità del distruttore, penso che il seguente fa il lavoro:

#include <type_traits> 

template <typename T> 
class Base { 
protected: 
    ~Base() { 
     static_assert(!std::is_convertible<T*,Base*>::value, "Invalid use of public inheritance."); 
    }  
}; 

class Derived : public Base<Derived> { 
}; 

int main() { 
    Derived d; 
} 

Il codice di cui sopra non riesce a compilare: i fuochi static_assert perché Derived* è convertibile in Base<Derived>*. Tuttavia, se si modifica l'ereditarietà su protected o private, il codice viene compilato.

Purtroppo gli utenti possono ancora si spara in un piede:

class Bad : public Base<Derived> { 
}; 
+0

Perché funziona quando si esegue l'ereditarietà privata? –

+0

@JerryJeremiah Se 'Derived' deriva pubblicamente da' Base 'quindi' std :: is_convertible *> :: value == true'. Quindi la negazione è 'false' e ​​la' static_assert' si attiva. Tuttavia, se l'ereditarietà è 'privata', allora' std :: is_convertible *> :: value == false'. –

+0

@CassioNeri grazie, questo sembra davvero fare il lavoro! Mi chiedo quanto sia "sicuro" usare "std :: is_convertible" quando si tratta ad es. con ereditarietà multipla ... ma ancora, non complichiamo il problema :) (in particolare, perché hai usato 'is_convertible' e non' is_base_of'? Ohh vedo, 'is_base_of' restituisce true per qualsiasi tipo di ereditarietà, non solo 'public'.) Inoltre, perché stai dicendo che devi avere un distruttore banale in' Base'? Sembra che posso aggiungere cose senza problemi, dopo l'asserzione statica. – vsoftco

0

Non sono sicuro se questo è quello che stai cercando o se questo vi aiuterà nel vostro caso. Mostrerò comunque un comportamento polimorfico.

Protetta di costruzione astratta Classe

class BaseProtected { 
// ----- Member Variable Section ----- 
public: 
    // There Shouldn't Be Public Variables In A Base Class Unless 
    // That Is The Behavior You Are Looking For - Keep In Mind 
    // Every Inherited Class & Outside Class Can Change Them. 
protected: 
    // Member Variables Here To Be Shared With Each Derived Class 
private: 
    // Member Variables Here To Be Used By Base Class Only 

// ----- Member Function Section ----- 
public: 
    virtual ~BaseProtected(); // Virtual Destructor 
    void somefunc() const; // Common Function Between All Derived Class 
    virtual void allDerivedMustImplement() const; = 0 // Purely Virtual 

protected: 
    // Default Constructor - Can Not Declare An Instance Of Base Class 
    BaseProtected(); // Abstract Class   

    // Protected Functions Shared Between Classes 
    // Protected Functions That Are Purely Virtual If Needed 

private: 
    // Private Functions Used By Base Class Only 

}; // BaseProtected 

classe derivata con l'ereditarietà Possibile

class DerivedWithPossibleInheritance : public BaseProtected { 
// ----- Member Variable Section ----- 
public: 
    // Public Member If Giving Free Access 
protected: 
    // Protected Members If Being Inherited From 
private: 
    // Private Members Unique To This Derived Class 

// ----- Member Function Section ----- 
public: 
    DerivedWithPossibleInheritance(); // Default Constructor 
    virtual ~DerivedWithPossibleInheritance(); // Virtual Destructor 

    void uniqueFunctionForThisClass() const; 
    void allDerivedMustImplement() const override; 

private: 
    // Private Functions Unique To This Class 

}; // DerivedWithPossibleInheritance 

classe derivata che non può essere ereditata da

class DerivedClassCanNotBeInheritedFrom sealed : public BaseProtected { 
// ----- Member Variable Section ----- 
public: 
    // Public Members Variables 
protected: 
    // Should Not Have Member Variables Here For This Class Can Not Be Inherited from 
private: 
    // Private Members Variables 

// ----- Member Function Section ------ 
public: 
    DerivedClassCanNotBeInheritedFrom(); // Default Constructor 
    virtual ~DerivedClassCanNotBeInheritedFrom(); // Default Virtual Destructor 

    void anotherUniqueFunctionForThisClass() const; 
    void allDerivedMustImplement() const override; 

protected: 
    // There Should Not Be Any Functions Here This Can Not Be Inherited From 

private:  
    // Private Member Functions Here 

}; // DerivedClassCanNotBeInheritedFrom 

Quello che ho dimostrato qui è la chiave parola sigillata quando si lavora con l'ereditarietà e il polimorfismo. Se non si desidera che la classe venga derivata, utilizzare la parola chiave sealed.

Come per dichiarare qualsiasi classe base, una classe autonoma o una classe singleton che ha un Costruttore privato è necessario l'uso della parola chiave friend. Ci sono molti tipi di implementazioni da coinvolgere qui per mostrarle, ma il concetto di impedire che una classe venga ereditata è lo stesso. Personalmente non ho usato la parola chiave final, ma ho usato la parola chiave sealed e funziona molto bene.

Non ho usato l'ereditarietà di classi diverse da quelle pubbliche: Quindi rispondere alle tue domande in termini di eredità privata o protetta non è qualcosa con cui sono veramente familiare. Ma forse l'uso della parola chiave sigillata potrebbe aiutarti.

+0

Grazie per lo sforzo, ma sfortunatamente 'sealed' è un'estensione per compilatore (se non sbaglio funziona solo su VS), e più di questo, C++ 11 ha introdotto' final' come parola chiave standard per prevenire l'ereditarietà (fondamentalmente lo stesso effetto di 'sealed'). Voglio comunque prevenire l'ereditarietà pubblica, ma consentire l'ereditarietà privata/protetta. – vsoftco

+0

Okay non ero sicuro al 100% ma sì sto lavorando con VS2012 e VS2013 –

+0

E sì capisco la tua domanda ma non ho mai avuto uno scenario per quel tipo di uso per cui rispondere alla domanda è un po 'oltre il mio proprie abilità. –

Problemi correlati