web-dev-qa-db-fra.com

Empêcher l'héritage de classe en C ++

Récemment, un de mes amis m'a demandé comment empêcher l'héritage de classe en C++. Il voulait que la compilation échoue.

J'y réfléchissais et j'ai trouvé 3 réponses. Je ne sais pas quel est le meilleur.

1) Constructeur (s) privé (s)

class CBase
{

public:

 static CBase* CreateInstance() 
 { 
  CBase* b1 = new CBase();
  return b1;
 }

private:

 CBase() { }
 CBase(CBase3) { }
 CBase& operator=(CBase&) { }


};

2) Utilisation de la classe de base CSealed, du ctor privé et de l'héritage virtuel

class CSealed
{

private:

 CSealed() {
 }

 friend class CBase;
};


class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

3) Utilisation d'une classe de base CSealed, d'un ctor protégé et d'un héritage virtuel

class CSealed
{

protected:

 CSealed() {
 }

};

class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

Toutes les méthodes ci-dessus garantissent que la classe CBase ne peut plus être héritée. Ma question est:

1) Quelle est la meilleure méthode? D'autres méthodes disponibles?

2) Les méthodes 2 et 3 ne fonctionneront que si la classe CSealed est héritée de manière virale. Pourquoi donc ? At-il quelque chose à voir avec vdisp ptr ??

PS:

Le programme ci-dessus a été compilé dans le compilateur MS C++ (Visual Studio). référence: http://www.codeguru.com/forum/archive/index.php/t-321146.html

56
ring0

Depuis C++ 11, vous pouvez ajouter le mot-clé final à votre classe, par exemple

class CBase final
{
...

La principale raison que je peux voir pour vouloir faire cela (et la raison pour laquelle je suis venu chercher cette question) est de marquer une classe comme non sous-classable afin que vous puissiez utiliser en toute sécurité un destructeur non virtuel et éviter complètement une vtable.

70
Peter N Lewis

Vous ne pouvez pas empêcher l'héritage (avant le mot clé final de C++ 11) - vous pouvez uniquement empêcher l'instanciation des classes héritées. En d'autres termes, il n'y a aucun moyen d'empêcher:

class A { ... };

class B : public A { ... };

Le mieux que vous puissiez faire est d'empêcher l'instanciation d'objets de type B. Cela étant le cas, je vous suggère de suivre les conseils de kts et de documenter le fait que A (ou quoi que ce soit) n'est pas destiné à être utilisé pour l'héritage, lui donner un destructeur non virtuel, et aucune autre fonction virtuelle, et le laisser ainsi.

11
anon

Vous passez par des contorsions pour empêcher d'autres sous-classements. Pourquoi? Documentez le fait que la classe n'est pas extensible et rendez le dtor non virtuel. Dans l'esprit de c, si quelqu'un veut vraiment ignorer la façon dont vous vouliez que cela soit utilisé, pourquoi les arrêter? (Je n'ai jamais vu l'intérêt de final classes/méthodes dans Java non plus).

//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
  DontExtened();
  /*NOT VIRTUAL*/
  ~DontExtened();
  ...
};
10
KitsuneYMG

1) est une question de goût. Si je le vois correctement, vos 2e et 3e solutions plus sophistiquées déplacent l'erreur dans certaines circonstances du temps de liaison au temps de compilation, ce qui devrait en général être mieux.

2) L'héritage virtuel est nécessaire pour forcer la responsabilité d'initialiser la classe de base (virtuelle) à la classe la plus dérivée d'où le ctor de classe de base n'est plus accessible.

5
Whoever

Pour répondre à votre question, vous ne pouvez pas hériter de CBase car dans l'héritage virtuel, une classe dérivée devrait avoir un accès direct à la classe dont elle a été héritée virtuellement. Dans ce cas, une classe qui dériverait de CBase devrait avoir un accès direct à CSealed, ce qu'elle ne peut pas puisque le constructeur est privé.

Bien que je ne vois pas l'utilité de tout cela (c'est-à-dire: arrêter l'héritage), vous pouvez généraliser à l'aide de modèles (je ne pense pas qu'il compile sur tous les compilateurs, mais il le fait avec MSVC)

template<class T>
class CSealed
{
    friend T;    // Don't do friend class T because it won't compile
    CSealed() {}
};

class CBase : private virtual CSealed<CBase>
{
};
4
Francis Boivin

Si vous le pouvez, je choisirais la première option (constructeur privé). La raison en est que pratiquement n'importe quel programmeur C++ expérimenté le verra en un coup d'œil et pourra reconnaître que vous essayez d'empêcher le sous-classement.

Il peut y avoir d'autres méthodes plus délicates pour empêcher le sous-classement, mais dans ce cas, le plus simple sera le mieux.

1
T.E.D.

Pour élaborer sur réponse de Francis : si la classe Bottom dérive de la classe Middle, qui hérite virtuellement de la classe Top, c'est la classe la plus dérivée (Bottom) qui est responsable de la construction de la classe de base virtuellement héritée (Top). Sinon, dans le scénario d'héritage multiple/diamant de la mort (où l'héritage virtuel est classiquement utilisé), le compilateur ne saurait pas laquelle des deux classes "moyennes" devrait construire la classe de base unique. L'appel du constructeur de Middle au constructeur de Top est donc ignoré lorsque Middle est construit à partir de Bottom:

class Top {
    public:
        Top() {}
}

class Middle: virtual public Top {
    public:
        Middle(): Top() {} // Top() is ignored if Middle constructed through Bottom()
}

class Bottom: public Middle {
    public:
        Bottom(): Middle(), Top() {}
}

Donc, dans l'approche 2) ou 3) de votre question, Bottom() ne peut pas appeler Top() car il est hérité en privé (par défaut, comme dans votre code, mais cela vaut la peine de faire il est explicite) dans Middle et n'est donc pas visible dans Bottom. ( source )

0
Blazej Czapp
class myclass;

    class my_lock {
        friend class myclass;
    private:
        my_lock() {}
        my_lock(const my_lock&) {}
    };

    class myclass : public virtual my_lock {
        // ...
    public:
        myclass();
        myclass(char*);
        // ...
    };

    myclass m;

    class Der : public myclass { };

    Der dd;  // error Der::dd() cannot access
            // my_lock::my_lock(): private  member

Je l'ai trouvé ici pour rendre hommage. Je poste ici juste d'autres personnes peuvent facilement accéder http://www.devx.com/tips/Tip/38482

0
Vijay