Je souhaiterais redéfinir l'opérateur = d'une classe qui hérite de std::vector<T*>. Dans cet opérateur je voudrais d'abord appeler l'opérateur = de std::vector<T*>. Comment puis-je faire ? (sous VC++ 6)
De manière générale pour appeler une méthode de la classe parente tu fais:
ClasseParente::methode(paramètres);
donc ici tu écris :
template<class T>
class MonVecteur : std::vector<T*>{
MonVecteur < T >& MonVecteur::operator=(const MonVecteur< T >& other){
std::vector<T*>::operator=(other);
//traitement
.
.
.
//fin traitement
return *this;
}
};
note : éviter l'héritage public est necessaire si tu veux garder la cohérence objet et éviter qu'un programmeur fasse appel à push_back() (ou autre méthode) qui n'a rien à voir avec ta nouvelle classe...
Il manque la protection contre l'auto-assignement:
template<typename T>
vecteur< T >& vecteur< T >::operator=( const vecteur< T >& rhs )
{
if ( this != &rhs ) {
std::vector<T*>::operator=(rhs);
//... do stuff
}
return *this;
}
Ok, c'est sous-optimal dans la plupart des cas.
Mais :
- c'est du KISS (Keep it simple stupid).
- il sera toujours temps d'optimiser, apres.
- dans le cas present, il me semble que ce n'est pas judicieux puisque cela fait intervenir un std::vector temporaire plus sa copie. Tout bon compilo devrait pouvoir optimiser cela (je suppose), mais...
De toute facon, saimal d'heriter des conteneurs de la STL.
puisque les methodes ne sont pas virtuelles, à commencer [par] les destructeurs
Ben voila, t'as la reponse :)
Puisque tu ne peux/dois pas surcharger les methodes du conteneur, et que son destructeur n'est pas virtuel, ca fait 2 bonnes raisons pour avoir une classe qui soit implementee par aggregation/composition plutot que par heritage.
Cela va même plus loin que cela. Il ne s'agit que de la partie syntaxico-visible de l'iceberg.
La question qu'il faut se poser est : "est-ce que les objets de la nouvelle classes seront des représentants de la classe ancêtre" <=> "peut-on substituer des objets fils là où l'on attend des objets du type parent?"
Bien souvent, on va se rendre compte que non -- dans le cas des conteneurs.
- s'il s'agit de rajouter des services, on peut les rajouter à côté. Ils feront toujours parti de l'interface publique, étendue de l'objet.
- Il est parfois acceptable (!= ultimement propre, ...) de dériver pour les rajouter tant qu'il n'y a pas de nouvelle donnée membre (pour éviter d'éventuels slicings), et que l'on ne joue pas à détruire polymorphiquement.
- s'il s'agit de modifier des services, ben ...on casse vite les contrats établis par le parent. L'exemple typique, c'est la définition d'une liste triée à partir d'un liste. Exemple typique où l'héritage (public en C++, quelconque en Java) n'est pas acceptable. Ici, on ne veut pas "être utilisé en place de" , on veut "réutiliser" => héritage privé ou délégation queconque
- s'il s'agit de spécialiser le conteneur pour des objets précis pour lesquels on veut un traitement suplémentaire bien précis, de nouveau, on bride le contrat initial. On veut réutiliser du code, pas être utilisés en place de.
Ici le polymorphisme d'inclusion (impliqué par l'héritage public) est rarement la solution si on veut factoriser le code. Au contraire du polymorphisme paramétrique et statique des templates du C++.
Tu n'es pas alllé voir mes liens ? Avoue. :P
L'aspect de pessimisation n'est qu'un détail qui n'a pas la moindre importante.Le problème est qu'il s'agit d'un contre-idiome qui résoud un problème théorique qui ne se produit jamais, et qui ne résoud pas un problème pratique qui se produit dans des cas aux limites liés à l'exécution. (!= code zarbi)Ce hack des années 80-90 n'est pas exception-safe. Alors qu'une solution qui est exception-safe résoud implicitement le problème d'auto-affectation. Hack qui n'est dû qu'aux codes qui préféraient désaouller avant de réallouer, ce qui empéchait d'avoir des opérations atomiques.Faire le test ne correspond alors plus qu'à une vaine tentative d'optimisation qui se résoud systématiquement en pessimisation -- sauf code pour le moins étrange.Et difficile de faire plus kiss que l'idiome de la copie par swap -- qui s'avère parfois lourd sur les objets ne se résumant à un pointeur encapsulé. Sachant qu'en plus il n'y a strictement aucun intérêt à définir les deux opérations de recopie pour les classes dont tous les membres ont déjà une sémantique de copie -- i.e. pas de ressource brute -- ce qui est encore bien plus dans la philo "kiss".
Par contre, j'etais en effet passe un peu (trop) vite sur les aspects d'exception-safety...
Garantir la condition de base (pour avoir du code exception-safe), c'est toujours bon a prendre.
Je vais essayer de m'astreindre a suivre cette implementation dans l'avenir.
PS: je viens de relire mon "Effective C++" de S.Meyers (3eme Ed.): il laggue un peu :)
PPS: et je suppose que tu veux dire: s/swap(rhs)/swap(temp)/
Pour le PS: je n'ai pas encore investi dans la 3ed du EC++. Qu'entends-tu pas "lagguer" ?
Nan... rien...
Je suis le capitaine bigleux.
Tout petit deja, j'avais ce probleme...
Tout ca pour dire, que, oui, dans son Item 11, S. Meyers recommande en effet d'utiliser la methode decrite plus haut pour gerer l'auto-assignement.
# Appels surchargés
Posté par ecyrbe . Évalué à 3.
ClasseParente::methode(paramètres);
donc ici tu écris :
template<class T>
class MonVecteur : std::vector<T*>{
};
note : éviter l'héritage public est necessaire si tu veux garder la cohérence objet et éviter qu'un programmeur fasse appel à push_back() (ou autre méthode) qui n'a rien à voir avec ta nouvelle classe...
[^] # Re: Appels surchargés
Posté par Sebastien . Évalué à 1.
[^] # Re: Appels surchargés
Posté par lmg HS (site web personnel) . Évalué à 2.
Ceci est un contre idiome qui se résume à une pessimisation.
http://c.developpez.com/faq/cpp/?page=surcharge#DIVERS_auto_(...)
http://www.gotw.ca/gotw/059.htm
[^] # Re: Appels surchargés
Posté par Sebastien . Évalué à 1.
Mais :
- c'est du KISS (Keep it simple stupid).
- il sera toujours temps d'optimiser, apres.
- dans le cas present, il me semble que ce n'est pas judicieux puisque cela fait intervenir un std::vector temporaire plus sa copie. Tout bon compilo devrait pouvoir optimiser cela (je suppose), mais...
De toute facon, saimal d'heriter des conteneurs de la STL.
[^] # Re: Appels surchargés
Posté par left . Évalué à 1.
[^] # Re: Appels surchargés
Posté par Sebastien . Évalué à 1.
Ben voila, t'as la reponse :)
Puisque tu ne peux/dois pas surcharger les methodes du conteneur, et que son destructeur n'est pas virtuel, ca fait 2 bonnes raisons pour avoir une classe qui soit implementee par aggregation/composition plutot que par heritage.
[^] # Re: Appels surchargés
Posté par lmg HS (site web personnel) . Évalué à 1.
La question qu'il faut se poser est : "est-ce que les objets de la nouvelle classes seront des représentants de la classe ancêtre" <=> "peut-on substituer des objets fils là où l'on attend des objets du type parent?"
Bien souvent, on va se rendre compte que non -- dans le cas des conteneurs.
- s'il s'agit de rajouter des services, on peut les rajouter à côté. Ils feront toujours parti de l'interface publique, étendue de l'objet.
- Il est parfois acceptable (!= ultimement propre, ...) de dériver pour les rajouter tant qu'il n'y a pas de nouvelle donnée membre (pour éviter d'éventuels slicings), et que l'on ne joue pas à détruire polymorphiquement.
- s'il s'agit de modifier des services, ben ...on casse vite les contrats établis par le parent. L'exemple typique, c'est la définition d'une liste triée à partir d'un liste. Exemple typique où l'héritage (public en C++, quelconque en Java) n'est pas acceptable. Ici, on ne veut pas "être utilisé en place de" , on veut "réutiliser" => héritage privé ou délégation queconque
- s'il s'agit de spécialiser le conteneur pour des objets précis pour lesquels on veut un traitement suplémentaire bien précis, de nouveau, on bride le contrat initial. On veut réutiliser du code, pas être utilisés en place de.
Ici le polymorphisme d'inclusion (impliqué par l'héritage public) est rarement la solution si on veut factoriser le code. Au contraire du polymorphisme paramétrique et statique des templates du C++.
[^] # Re: Appels surchargés
Posté par lmg HS (site web personnel) . Évalué à 1.
Tu n'es pas alllé voir mes liens ? Avoue. :P L'aspect de pessimisation n'est qu'un détail qui n'a pas la moindre importante.
Le problème est qu'il s'agit d'un contre-idiome qui résoud un problème théorique qui ne se produit jamais, et qui ne résoud pas un problème pratique qui se produit dans des cas aux limites liés à l'exécution. (!= code zarbi)
Ce hack des années 80-90 n'est pas exception-safe. Alors qu'une solution qui est exception-safe résoud implicitement le problème d'auto-affectation. Hack qui n'est dû qu'aux codes qui préféraient désaouller avant de réallouer, ce qui empéchait d'avoir des opérations atomiques.
Faire le test ne correspond alors plus qu'à une vaine tentative d'optimisation qui se résoud systématiquement en pessimisation -- sauf code pour le moins étrange.
Et difficile de faire plus kiss que l'idiome de la copie par swap -- qui s'avère parfois lourd sur les objets ne se résumant à un pointeur encapsulé. Sachant qu'en plus il n'y a strictement aucun intérêt à définir les deux opérations de recopie pour les classes dont tous les membres ont déjà une sémantique de copie -- i.e. pas de ressource brute -- ce qui est encore bien plus dans la philo "kiss".
[^] # Re: Appels surchargés
Posté par lmg HS (site web personnel) . Évalué à 1.
s/swap(rhs)/swap(other)/
Bien évidemment.[^] # Re: Appels surchargés
Posté par Sebastien . Évalué à 1.
Si-si, mais je prefere celui-la :)
http://www.parashift.com/c++-faq-lite/assignment-operators.h(...)
Par contre, j'etais en effet passe un peu (trop) vite sur les aspects d'exception-safety...
Garantir la condition de base (pour avoir du code exception-safe), c'est toujours bon a prendre.
Je vais essayer de m'astreindre a suivre cette implementation dans l'avenir.
PS: je viens de relire mon "Effective C++" de S.Meyers (3eme Ed.): il laggue un peu :)
PPS: et je suppose que tu veux dire:
s/swap(rhs)/swap(temp)/
[^] # Re: Appels surchargés
Posté par lmg HS (site web personnel) . Évalué à 1.
C'est plus clair dans XP++/GOTW et le coding standards de H.Sutter & A.Alexandrescu.
Pour le PS: je n'ai pas encore investi dans la 3ed du EC++. Qu'entends-tu pas "lagguer" ?
Pour le PPS: pfff en effet. :-/
[^] # Re: Appels surchargés
Posté par Sebastien . Évalué à 0.
Nan... rien...
Je suis le capitaine bigleux.
Tout petit deja, j'avais ce probleme...
Tout ca pour dire, que, oui, dans son Item 11, S. Meyers recommande en effet d'utiliser la methode decrite plus haut pour gerer l'auto-assignement.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.