Question

Il existe une restriction selon laquelle une classe ne peut pas servir de classe de base pour plus de 7 classes. Existe-t-il un moyen d’appliquer la règle ci-dessus au moment de la compilation?

Je connais la technique Usable_Lock d'Andrew Koenig pour empêcher l'héritage d'une classe, mais elle échouera uniquement lorsque nous essayons d'instancier la classe. Cela ne peut-il pas être fait en dérivant?

La classe de base est autorisée à savoir qui sont ses enfants. Donc, je suppose que nous pouvons déclarer une combinaison d’amis classes et les encapsuler pour appliquer cette règle. Supposons que nous essayions quelque chose comme ça

class AA {
   friend class BB;
   private:
      AA() {}
      ~AA() {}
};

class BB : public AA {

};

class CC : public AA 
{};

La dérivation de la classe CC générerait un avertissement du compilateur concernant un fichier inaccessible. Nous pouvons alors signaler tels que des avertissements tels que des erreurs lors de l’ajustement des réglages du compilateur (par exemple, marquer tous les avertissements comme des erreurs), mais je ne voudrais pas utiliser de telles techniques.

Une autre solution, mais qui me semble plutôt maladroite est: -

class B;

class InheritanceRule{
    class A {
    public:
        A() {}
        ~A() {}
    };
    friend class B;
};

class B {
public:
    class C : public InheritanceRule::A
    {};
};


class D : public InheritanceRule::A{};

La dérivation de la classe D sera signalée comme une erreur du compilateur, ce qui signifie que toutes les classes à dériver doivent être dérivées de la classe B. Cela permettra au moins d'inspecter le nombre de classes dérivées de la classe A mais n'empêchera personne d'ajouter plus.

Quelqu'un ici a-t-il un moyen de le faire? Mieux encore, si la classe de base n'a pas besoin de savoir qui sont ses enfants.

NOTE: La classe qui fait office de classe de base peut elle-même être instanciée (ce n'est pas abstrait).

Merci d'avance,

EDIT-1: Selon le commentaire de jon.h, une légère modification

// create a template class without a body, so all uses of it fail
template < typename D> 
class AllowedInheritance;

class Derived; // forward declaration
// but allow Derived by explicit specialization 
template<> 
class AllowedInheritance< Derived> {};

template<class T>
class Base : private AllowedInheritance<T> {};

// privately inherit Derived from that explicit specialization    
class Derived : public Base<Derived> {};

// Do the same with class Fail Error
// it has no explicit specialization, so it causes a compiler error
class Fail : public Base<Fail> {}; // this is error

int main()
{   
   Derived d;

   return 0;
}
Était-ce utile?

La solution

Je suis fatigué comme de la merde, je peux à peine garder les yeux ouverts. Il existe donc probablement un moyen plus élégant de le faire, et je n'approuve certainement pas l'idée bizarre qu'une base devrait avoir au plus sept sous-classes.

// create a template class without a body, so all uses of it fail
template < typename D, typename B> class AllowedInheritance;


class Base {};
class Derived; // forward declaration

// but allow Derived, Base by explicit specialization 

template<> class AllowedInheritance< Derived, Base> {};

// privately inherit Derived from that explicit specialization    
class Derived : public Base, private AllowedInheritance<Derived, Base> {};


// Do the same with class Compiler Error
// it has no explicit specialization, so it causes a compiler error
class CompileError: public Base, 
     private AllowedInheritance<CompileError, Base> { };

//error: invalid use of incomplete type 
//‘struct AllowedInheritance<CompileError, Base>’


int main() {

   Base b;
   Derived d;
   return 0;
}

Commentaire de jon.h:

  

Comment cela s'arrête-t-il, par exemple: class Fail: base publique {}; ? \

Ce n'est pas. Mais l’exemple original du PO ne l’a pas non plus.

Pour l'OP: votre révision de ma réponse est à peu près une application simple du " de Coplien ; Modèle de modèle curieusement récurrent & Quot; ]

Je pensais aussi à cela, mais le problème à ce sujet n’existe pas de relation d’héritage entre un derived1 : pubic base<derived1> et un derived2 : pubic base<derived2>, car base<derived1> et base<derived2> sont deux classes totalement indépendantes.

Si votre seul souci est l'héritage d'implémentation, ce n'est pas un problème, mais si vous voulez l'héritage d'interface, votre solution le résout.

Je pense qu'il existe un moyen d'obtenir à la fois un héritage et une syntaxe plus propre. comme je l'ai mentionné, j'étais assez fatigué lorsque j'ai écrit ma solution. Si rien d’autre, faire de RealBase une classe de base de Base dans votre exemple constitue une solution miracle.

Il existe probablement plusieurs façons de résoudre ce problème. Mais je tiens à souligner que je suis d'accord avec markh44: même si ma solution est plus propre, nous encombrons toujours le code pour soutenir une règle qui n'a pas de sens. Ce n’est pas parce que cela peut être fait que cela devrait se faire.

Si la classe de base en question a dix ans et est trop fragile pour en hériter, la vraie solution consiste à la réparer.

Autres conseils

Désolé, je ne sais pas comment imposer une telle limite à l'aide du compilateur.

Personnellement, je ne voudrais pas essayer de forcer la règle dans le code lui-même - vous encombrez le code avec des éléments qui n'ont rien à voir avec ce que le code fait - ce n'est pas du code propre.

Plutôt que de sauter dans les cerceaux, j’essayais d’assouplir cette règle. Au lieu de cela, il devrait s'agir d'une directive qui pourrait être brisée si nécessaire et en accord avec les autres membres de l'équipe.

Bien sûr, je ne sais pas exactement ce que vous faites, donc la règle pourrait être appropriée, mais en général, ce n'est probablement pas le cas.

Toute programmation " rule " cela dit que vous ne devez jamais faire x ou que vous devez toujours faire y est presque toujours faux! Remarquez le mot & "Presque &"; dedans là.

Parfois, vous pourriez avoir besoin de plus de 7 classes dérivées - que faites-vous alors? Sauter à travers plus de cerceaux. Aussi, pourquoi 7? Pourquoi pas 6 ou 8? C'est tellement arbitraire - un autre signe d'une mauvaise règle.

Si vous devez le faire, comme le dit JP, l'analyse statique est probablement la meilleure solution.

De nombreux outils d’analyse de code statique fournissent des informations sur la hiérarchie des héritages. Plutôt que d'essayer de le gérer dans votre code, je me pencherais sur un outil qui pourrait définir des règles pour la hiérarchie d'héritage et échouer lors de la construction si ces règles ne sont pas suivies. Cela peut coûter un peu de dollars et vous devrez peut-être écrire une règle personnalisée (j'ai vu la profondeur de l'héritage, mais pas celle-ci & "Largeur &" Comme vous le souhaitez). Mais, à long terme, je pense que c'est votre meilleur pari.

Par commentaire: j'ai utilisé Coverity avec un certain succès. Peu dispendieux. Il existe plusieurs bons threads SO de meilleures options.

Plutôt que d'encombrer le code d'assertions, vous pourriez peut-être utiliser quelque chose comme GCC-XML , qui analyse la source C ++ à l'aide de l'interface frontale du compilateur g ++ et génère une sortie XML. Je m'attends à ce qu'il soit raisonnablement simple de développer un outil qui analyse ce résultat et vérifie les violations de la règle; cela pourrait alors être intégré à l'enregistrement du code source.

BTW, informer les classes de base de leurs descendants enfreint le Principe d'ouverture / fermeture . , ce qui signifie qu’elle réduit l’utilité de la programmation OO en général. La raison principale de séparer le code en classes et sous-classes de base est telle que la classe de base ne doit pas nécessairement connaître ses sous-classes - cela rend possible des choses comme les packages de plug-in livrés après installation.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top