Bonjour,
J'ai une classe A :
Lequel des deux constructeurs et le plus rapide ?Code:
1
2
3
4
5
6
7
8 class A { private: double _d; public: A(double a) { _d = a } A(const double & a); { _d = a; } };
Merci.
Version imprimable
Bonjour,
J'ai une classe A :
Lequel des deux constructeurs et le plus rapide ?Code:
1
2
3
4
5
6
7
8 class A { private: double _d; public: A(double a) { _d = a } A(const double & a); { _d = a; } };
Merci.
Aucun, les deux seront compilés de la même manière la plus optimale, vu qu'il s'agit d'un type primitif, sauf sur une architecture mal fichue.
Cela, grâce à la règle du "as if", qui dit qu'un compilateur peut faire ce qu'il veut, tant que tu ne vois pas la différence avec les exigences de la norme.
De toute façon, le plus rapide sera normalement le suivant:
Ce constructeur aura lui l'avantage de ne pas initialiser _d avant de le réaffecter.Code:A(double const& a) : _d(a) {}
dans ce cas :
b est plus rapide que a ? on évite une recopie ?Code:
1
2
3
4
5
6 int fonction() { return 2;} int main() { int a = function(); int & b = function(); }
Ça ne compilera pas car une référence ne peut pas être initialisée avec une rvalue (mais une référence constante si (allongement de la durée de vie et des problèmes si mal utilisée)).
Mais sinon, le compilateur optimisera avec une élision (voir RVO).
Ca, c'est tout un programme, qui dépendra essentiellement de la signature complete de function et de l'origine de la valeur qu'elle renvoie...
De manière générale : si function renvoie une valeur, nous avons la certitude que int a = function() fonctionnera, par contre, int & b = function() aura un problème car b sera une référence invalide (car la durée de vie de la variable renvoyée par function ne sera pas élargie à une variable déclarée dans la fonction appelante).
D'un autre coté, function pourrait renvoyer une référence. Mais il faut alors voir quelle est l'origine de la variable qui est renvoyée par référence. S'il s'agit d'une référence sur une variable propre à function proche de
les deux codes poseront problèmes car la référence est invalidée par la destruction de la variable qui survient lorsque l'on quitte la fonction.Code:
1
2
3
4
5 double /*const*/ & function(){ double d; /* ... */ return d; // oupppssss... }
Par contre, si la référence renvoyée est issue "d'ailleurs" sous une forme qui pourrait être proche de
on pourra avoir quelque chose de valide aussi bien pour la valeur a que pour la référence b. Et, a priori, b sera alors plus rapide parce qu'il n'y a pas copie.Code:
1
2
3
4 double & function (double & d){ /* ... */ return d; }
Par contre, il n'y a pas de sens à s'inquiéter des éventuelles pertes de performances dues à la copie d'une variable de type primtif par rapport au passage par référence, car il n'y a aucun avantage mesurable (la copie des type primitifs est très rapide, et une référence est représentée au niveau du code exécutable, par une adresse, qui devra de toutes façon être donnée et qui prendra autant de temps à être écrite en mémoire que ce qu'un double peut nécessiter pour être copié).
L'idée est donc, pour les types primitifs et / ou les structures dont la taille est plus petite qu'un ptr_t, de les manipuler systématiquement par valeur, sauf s'il s'agit de les transmettre à une fonction qui devra modifier la valeur d'origine et, à ce moment là, de les passer par référence non constante.
Pour les types de taille plus importante, la règle est de les transmettre systématiquement par référence et de rendre la référence constante si la variable d'origine ne doit pas être modifiée par la fonction appelée.
Par contre, lorsqu'il s'agit de renvoyer ces donnée en sortie de fonction, il faudra s'assurer que la durée de vie de l'élément renvoyé sera suffisante. Ce parfois le cas, parfois pas ;)
Justement, non, cela compilera...
L'élément renvoyé par une fonction est systématiquement une... lvalue :P. Les deux possibilités compileront donc et les deux possibilités subiront éventuellement les mêmes problèmes en fonction de ce qui est réellement renvoyé par function :aie:
Au temps pour moi, je dis une énormité.
double & b = function(); compilera bien, pour autant que la const-correctness soit respectée. La raison en est que le compilateur sait se trouver à droite de l'opérateur d'affectation et qu'il s'attend donc à trouver... une rvalue (qui, basiquement, signifie "quelque chose susceptible de prendre place à droite de l'opérateur d'affectation" ;)).
La valeur renvoyée par une fonction peut, quoi qu'il arrive, effectivement prendre place à droite de l'opérateur d'affectation et le compilateur n'a donc aucune raison de refuser cette affectation.
Par contre, le fait que le code compile ne signifie absolument pas qu'il s'exécutera sans problème :aie:. Et le fait est que le compilateur ne dispose que de la signature de la fonction appelée pour se faire un idée de "ce qui fonctionnera" et de "ce qui n'ira pas".
Le problème, c'est que ce n'est pas assez pour pouvoir se faire une idée :aie:. Il y a en effet trois solutions :
- La fonction renvoie une valeur : le compilateur pourrait éventuellement nous indiquer que nous affectons un temporaire à notre référence. Mais il ne le fera que sous la forme d'un avertissement.
- La fonction renvoie une de ses variables propres sous la forme d'une référence : la référence est invalide, le compilateur peut indiquer que la fonction appelée renvoie une référence sur un temporaire, mais ce n'est encore qu'un avertissement.
- La fonction renvoie un argument qu'elle a reçu sous la forme d'une référence ou un membre de la classe au départ duquel la fonction a été appelée : il n'y a aucun problème et tout va bien... Si ce n'est que c'est le genre d'information qui nécessiterait l'analyse du code de la fonction appelée pour pouvoir s'en assurer. Et le compilateur n'est, a priori, pas équipé pour faire cette vérification au niveau de la fonction appelante :P
Donc, oui, il y a toujours un manque de garantie quant au fait que la référence soit bel et bien valide, mais cela n'occasionnera, au mieux, qu'un avertissement de la part du compilateur avec comme résultat le fait que seule des options de compilations particulièrement strictes (je pense, par exemple, à l'option -werror sous gcc) occasionneront réellement une erreur de compilation ;)
Donc j'ai bien le droit de faire :
Comme j'ai le droit de faire :Code:
1
2 (const?) objet & A = construire_objet(); int value = A.get_value();
Code:int value = construire_objet().get_value();
La réponse tient en deux mots : oui mais :aie:
Oui, tu as le droit de le faire dans le sens où le compilateur n'y verra aucune objection.
Mais, pour que A soit une référence valide, il faut:
- que construire_objet() renvoie une référence
- que la référence renvoyée par construire_objet fasse référence à un objet qui continue à exister en dehors de cette fonction (autrement dit, que la référence renvoyée par construire_objet() soit valide)
Et ça, c'est très loin d'être gagné :aie:
Il est possible de se donner les moyen de garantir que ce sera le cas (en plaçant l'objet créé dans une collection dont on sait qu'elle continuera d'exister en dehors de construire_objet, par exemple), mais, de manière générale, il faut vraiment voir ce que fait exactement la fonction appelée (construire_objet() en l'occurrence) pour pouvoir te dire si la référence renvoyée par cette fonction est valide ou non.
On va donc dire : on peut le faire (c'est légal en C++), mais, a priori, il y a de fortes chances pour que cela pose problème (du moins, sans avoir un exemple précis de ce que fait la fonction appelée pour initialiser ta référence-