Bonjour,
Avec les nouveauté du C++11. Je me demandais si il est possible de faire de l'inversion de contrôle sans avoir à faire de l'allocation dynamique.
Je pensais à l'utilisation de std::move et héritage mais ça ne marche pas.
Vous avez une idée?
Bonjour,
Avec les nouveauté du C++11. Je me demandais si il est possible de faire de l'inversion de contrôle sans avoir à faire de l'allocation dynamique.
Je pensais à l'utilisation de std::move et héritage mais ça ne marche pas.
Vous avez une idée?
Bonjour,
Ça veut dire quoi "faire de l'inversion de controle" ?
Hello,
L'idée c'est d'avoir une décorrélation entre l'instanciation d'un objet et sont utilisation.
On pourrait utiliser l'allocation dynamique mais je me dis que pour des objets qui nécessitent peu de mémoire ce serait beaucoup moins intéressant d'utiliser la heap.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 class IAnimal { public: virtual void Mange() = 0; }; void Programme() { IAnimal iAnimal = GetAnimal(...); animal.Mange(); }
selon ton exemple, cela s'appelle juste du polymorphisme... Pour réaliser une résolution dynamique des liens tu n'as pas d'autre choix d'utiliser un pointeur ou une référence.
Non, l'inversion de contrôle ne se réduit pas au polymorphisme, elle consiste à décharger le code client utilisateur d'une hiérarchie de la responsabilité de choisir ce qui est instancié. La cas idéal est de ne donner au code client que la connaissance d'un interface (donc en c++ une classe abstraite ne contenant que des fonctions virtuelles pures). Un code dédié se charge de retourner des instances de classes d'implémentation adaptées au cas (par exemple à la configuration, ou en fonction du chargement de plugins).
La notion d'inversion de contrôle est donc plus liée à la notion de factory.
A priori, l'inversion de contrôle ne nécessite pas en soi l'allocation dynamique, puisqu'on peut renvoyer un pointeur ou une référence à partir d'un objet membre à condition de ne pas faire de c*** avec les durées de vie...
Après, si tu cherches à configurer tes factories en fonction de paramètres runtime, ça devient plus difficile d'éviter l'allocation dynamique, mais ce n'est pas forcément infaisable.
Salut,Il n'y a rien à faire:
Une grande partie de l'inversion de contrôle passe par le polymorphisme: le fait qu'un comportement qui est disponible au niveau de la classe de base puisse être adapté au type réel de l'objet manipulé, qui correspond à un type dérivé de la classe de base.
Le fait est que tu ne peux profiter du polymorphisme, en C++, qu'au travers de pointeurs ou de références!
Tu pourrais donc avoir un code proche de
pour autant que GetAnimal renvoie une référence (éventuellement constante) sur une instance particulière de IAnimal.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 class IAnimal { public: virtual void Mange() = 0; }; void Programme() { IAnimal /* const */ & iAnimal = GetAnimal(...); animal.Mange(); }
Le problème vient alors de la durée de vie des variables (non statiques) pour lesquelles on n'a pas eu recours à l'allocation dynamique de la mémoire d'une part et à la sémantique d'entité qui est associée aux classes qui interviennent dans une hiérarchie d'objet d'autre part.
En effet, les variables non statiques pour lesquelles on n'a pas recours à l'allocation dynamique sont automatiquement détruites lorsque l'on atteint l'accolade fermante '}' de la portée dans laquelle les variables sont déclarées.
Si tu as une classe Chien héritant de IAnimal sous la forme de
un code proche de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 clas Chien : public IAnimal{ public: /* ... */ };
te claquera systématiquement dans les pattes parce que la référence renvoyée fera référence à... un objet qui a été détruit.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 IAnimal & GetAnimal(/* ... */){ Chien c(/*... */ ); return c; } // c est détruit ici
On pourrait envisager de créer une instance statique de chien, sous la forme de
mais, à chaque fois que tu ne pourrais alors avoir qu'un et un seul chien.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 IAnimal & GetAnimal(/* ... */){ static Chien c(/*... */ ); return c; }
L'alternative est alors de créer un type contenant un tableau de chiens et de veiller à placer l'objet à chaque fois dans le tableau, sous la forme de
parce qu'il n'est pas possible de créer une collection de références.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 class AnimalFactory{ public: IAnimal& getAnimal(/* ...*/){ if(/* c'est un chien */ ){ return createChien(/* ... */); } } private: IAnimal & createChien(/*... */){ chiens_.push_back(Chien(/* ... */ ); return chiens_[chiens_.size()-1]; } std::vector<Chien> chiens_; };
Tu serais donc obligé de maintenir une collection pour chaque type d'animal spécifique, et tu serait malgré tout limité, pour la durée de validité de ta référence, à la durée de vie de la factory sous une forme proche de
Mais, de plus, ce code se heurte de plein fouet à la sémantique d'entité associée aux classes qui interviennent dans une hiérarchie de classes!
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 int main(){ if(une condition){ AnimalFactory factory; IAnimal & animal = factory.GetAnimal(/* ... */); } // factory est détruit ici. La référence animal est invalidée à cause de ca return 0; }
En effet, une classe ayant sémantique d'entité n'est, par nature, ni copiable ni assignable.
Cela signifie que ta classe IAnimal devrait prendre la forme de (C++11 inside)
et que toutes les classes qui héritent (de manière directe ou indirecte) de IAnimal seront elles aussi non copiable et non assignable.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 class IAnimal { public: IAnimal(IAnimal const &) = delete; IAnimal& operator = (IAnimal const &) = delete; virtual void Mange() = 0; };
Sans recourir à la sémantique de mouvement, il te sera impossible de rajouter ton chien dans la collection de chien, parce qu'il n'est pas copiable.
Et le fait d'utiliser un tableau "C style" comme collection ne changera rien: un chien n'est pas assignable
Tu te retrouves donc "obligé", si tu veux disposer de différents animaux en les connaissant comme des IAnimal, de gérer leur durée de vie sous la forme de pointeurs, et donc de recourir à l'allocation dynamique.
Ceci dit, C++11 dispose maintenant de classes de pointeurs RAIIsantes, et il serait sans doute intéressant d'envisager d'utiliser un std::unique_ptr<Chien> (ou std::unique_ptr<IAnimal>, selon ce qui t'intéresse le plus) dans la collection qui maintient les différentes instances de chiens (ou d'animaux)
L'idée est alors d'utiliser le pointeur nu (IAnimal * ) renvoyé par la fonction get de unique_ptr chaque fois que tu a "juste" besoin de pouvoir disposer de l'objet, et de te dire que tu n'as pas besoin de t'inquiéter de la durée de vie de l'objet sous-jacent![]()
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Partager