web-dev-qa-db-fra.com

Comment définir une classe scellée en C++?

Comment arrêter la classe pour qu'elle soit héritée par une autre classe.

51
Mahantesh

Solution C++ 11

En C++ 11, vous pouvez sceller une classe en utilisant le mot clé final dans la définition, comme suit:

class A final  //note final keyword is used after the class name
{
   //...
};

class B : public A  //error - because class A is marked final (sealed).
{                   //        so A cannot be derived from.
   //...
};

Pour connaître les autres utilisations du final, voir ma réponse ici:


Solution C++ 03

Code de Bjarne Stroustrup : Puis-je empêcher les personnes qui dérivent de ma classe?

class Usable;
class Usable_lock {
    friend class Usable;
private:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock {
public:
    Usable();
    Usable(char*);
};
Usable a;

class DD : public Usable { };

DD dd;  // error: DD::DD() cannot access
        // Usable_lock::Usable_lock(): private  member

Generic_lock

Nous pouvons donc utiliser un modèle pour rendre le Usable_lock suffisamment générique pour sceller toute classe:

template<class T>
class  Generic_lock 
{
    friend T;
    Generic_lock() {}                     //private
    Generic_lock(const Generic_lock&) {}  //private
};

class Usable : public virtual Generic_lock<Usable>
{
public:
    Usable() {}
};

Usable a; //Okay
class DD : public Usable { };

DD dd; //Not okay!
82
Nawaz

Il y a deux façons, la simple bon marché et la bonne. Les deux réponses de @Naveen et de @Nawaz traitent de la bonne, ce qui nécessite la création manuelle d'un sealer class pour chaque classe que vous voulez réellement sceller.

Le moyen non infaillible, utilisé dans les bibliothèques Adobe, utilise une classe basée sur des modèles pour cela. Le problème est que vous ne pouvez pas déclarer l'argument de modèle en tant qu'ami, ce qui signifie que vous devrez passer de private à la moins sûre protected:

template <typename T>
class sealer {
protected: sealer() {}
};
class sealed : virtual sealer<sealed> {};

Et vous pouvez l’automatiser avec une macro (je ne me souviens pas de la saveur exacte de la macro dans le code d’Adobe):

#define seal( x ) virtual sealer<x>
class sealed : seal(sealed) 
{};

Maintenant, cela va attraper les gens qui tentent par erreur d’hériter sans savoir qu’ils ne devraient pas le faire:

class derived : sealed {};
int main() {
   derived d;  // sealer<T>::sealer() is protected within this context
}

Mais cela n'empêchera pas les personnes qui vraiment veulent dériver, car elles peuvent accéder au constructeur en dérivant elles-mêmes du modèle:

class derived : sealed, sealer<sealed> {};
int main() {
   derived d;
};

Je ne sais pas si cela changera dans C++ 0x, je pense me souvenir de discussions sur la possibilité pour un modèle de classe de créer un lien avec l'un de ses arguments, mais je ne peux pas vraiment dire en cherchant rapidement dans le projet Si cela était autorisé, ce serait une solution générique:

template <typename T>
class sealer {
   sealer() {}
   friend class T; // Incorrect in C++03
};

C++ 11 ajoute la possibilité d'empêcher l'héritage des classes ou simplement d'empêcher les méthodes de substitution dans les classes dérivées. Ceci est fait avec l'identifiant spécial final. Par exemple:

class Base final { };

class Derived1 : Base { }; // ill-formed because the class Base has been marked final

ou

class Base {
    virtual void f() final;
};

class Derived : Base {
    void f(); // ill-formed because the virtual function Base::f has been marked final

Notez que final n'est pas un mot-clé de langue. C'est techniquement un identifiant; il gagne seulement une signification spéciale lorsqu'il est utilisé dans ces contextes spécifiques. À n'importe quel autre endroit, il peut s'agir d'un identifiant valide.

7
AzP

Sur la base de http://www.stroustrup.com/bs_faq2.html#no-derivation FAQ De Bjarne Stroustrup avec une petite modification sans l'utilisation du mot clé ami:

// SEALED CLASS DEFINITIONS
class Usable_lock {
protected:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};
#define sealed_class private virtual Usable_lock

// SEALED CLASS USAGE EXMAPLES
class UsableLast : sealed_class {
public:
    UsableLast(){}
    UsableLast(char*){}
};
class DD : public UsableLast {};

// TEST CODE
template <class T> T createInstance() {
    return T();
}
int main()
{
    createInstance<UsableLast>();
//  createInstance<DD>();
    return 0;
}
0
bruziuz