Méthodes de copie d’un membre

J’ai une classe de conteneur simple et de bas niveau qui est utilisée par une classe de fichier de plus haut niveau. Fondamentalement, la classe de fichiers utilise le conteneur pour stocker les modifications localement avant d’enregistrer une version finale dans un fichier réel. Par conséquent, certaines méthodes sont directement transférées de la classe conteneur à la classe fichier. (Par exemple, Resize() .)

Je viens de définir les méthodes dans la classe de fichiers pour appeler leurs variantes de classe de conteneur. Par exemple:

 void FileClass::Foo() { ContainerMember.Foo(); } 

Cela devient cependant une nuisance. Y a-t-il une meilleure manière de faire cela?

Voici un exemple simplifié:

 class MyContainer { // ... public: void Foo() { // This function directly handles the object's // member variables. } } class MyClass { MyContainer Member; public: void Foo() { Member.Foo(); // This seems to be pointless re-implementation, and it's // inconvenient to keep MyContainer's methods and MyClass's // wrappers for those methods synchronized. } } 

Pourquoi ne pas simplement hériter de manière privée de MyContainer et exposer les fonctions que vous souhaitez simplement transmettre avec une déclaration using ? Cela s’appelle “Implémenter MyClass en termes de MyContainer .

 class MyContainer { public: void Foo() { // This function directly handles the object's // member variables. } void Bar(){ // ... } } class MyClass : private MyContainer { public: using MyContainer::Foo; // would hide MyContainer::Bar void Bar(){ // ... MyContainer::Bar(); // ... } } 

Désormais, “l’extérieur” pourra appeler directement Foo , tandis que Bar n’est accessible qu’à l’intérieur de MyClass . Si vous créez maintenant une fonction portant le même nom, la fonction de base est masquée et vous pouvez envelopper les fonctions de base de cette manière. Bien sûr, vous devez maintenant qualifier pleinement l’appel à la fonction de base, sinon vous entrerez dans une récursion sans fin.


En outre, si vous souhaitez autoriser le sous- MyClass (non polymorphe) de MyClass , il s’agit d’un des rares endroits où l’inheritance est protégé:

 class MyClass : protected MyContainer{ // all stays the same, subclasses are also allowed to call the MyContainer functions }; 

Non polymorphe si votre MyClass n’a pas de destructeur virtuel.

Oui, maintenir une classe de proxy comme celle-ci est très agaçant. Votre IDE pourrait avoir des outils pour le rendre un peu plus facile. Ou vous pourrez peut-être télécharger un add-on IDE.

Mais cela n’est généralement pas très difficile, sauf si vous devez prendre en charge des dizaines de fonctions, de substitutions et de modèles.

Je les écris habituellement comme:

 void Foo() { return Member.Foo(); } int Bar(int x) { return Member.Bar(x); } 

C’est sympa et symésortingque. C ++ vous permet de renvoyer des valeurs nulles dans les fonctions void, car cela améliore le fonctionnement des modèles. Mais vous pouvez utiliser la même chose pour rendre le code plus joli.

C’est l’ inheritance de la délégation et je ne sais pas si le C ++ offre un mécanisme pour aider à cela.

Considérez ce qui a du sens dans votre cas: la composition (a) ou l’inheritance (est) une relation entre MyClass et MyContainer.

Si vous ne souhaitez plus avoir un code comme celui-ci, vous êtes pratiquement limité à l’inheritance d’implémentation (MyContainer en tant que classe de base base / abstraite). Cependant, vous devez vous assurer que cela a du sens dans votre application et que vous n’héritez pas uniquement pour l’implémentation (l’inheritance pour l’implémentation est mauvais).

En cas de doute, ce que vous avez va probablement bien.

EDIT: Je suis plus habitué à penser en Java / C # et à négliger le fait que C ++ possède la plus grande flexibilité d’inheritance utilisée par Xeo dans sa réponse. Cela semble être une bonne solution dans ce cas.

Cette fonctionnalité dont vous avez besoin pour écrire de grandes quantités de code est en fait une fonctionnalité nécessaire. C ++ est un langage commenté, et si vous essayez d’éviter d’écrire du code avec c ++, votre conception ne sera jamais très bonne.

Mais le vrai problème avec cette question est que la classe n’a aucun comportement. C’est juste un emballage qui ne fait rien. Chaque classe doit faire autre chose que simplement transmettre des données.

L’essentiel est que chaque classe possède une interface correcte. Cette exigence rend nécessaire l’écriture de fonctions de transfert. L’objective principal de chaque fonction membre est de dissortingbuer le travail requirejs à tous les membres de données. Si vous n’avez qu’un seul membre de données et que vous n’avez pas encore décidé ce que la classe est censée faire, tout ce que vous avez est de transférer des fonctions. Une fois que vous avez ajouté plusieurs objects membres et décidé ce que la classe est censée faire, vos fonctions de transfert deviennent alors plus raisonnables.

Une chose qui aidera avec ceci est de garder vos classes petites. Si l’interface est petite, chaque classe de proxy n’aura qu’une petite interface et l’interface ne changera pas très souvent.