Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Probleme de comprehension : constructeur de copie (templates?)

53 réponses
Avatar
Alex Paris
Bonjour =E0 tous !

Voil=E0 mon petit exemple que je compile en utilisant g++ (cygwin)


#include <iostream>

using namespace std;

template<class T>
class Matrix
{
public:
Matrix(int size);
Matrix(const Matrix<T> &copy);

~Matrix();

Matrix<T>& operator=3D(const Matrix<T>& matrix);

Matrix<T> operator+(const Matrix<T>& second);

private:
int _size;
};

template <class T>
Matrix<T>::Matrix(int size)
: _size(size)
{
cout << "--- Constructor called --- " << endl;
}

template <class T>
Matrix<T>::Matrix(const Matrix<T> &copy)
: _size(copy._size)
{
cout << "--- Copy constructor called --- " << endl;
}

template <class T>
Matrix<T>::~Matrix()
{
cout << "--- Destructor called --- " << endl;
}

template <class T>
Matrix<T>& Matrix<T>::operator=3D(const Matrix<T>& matrix)
{
cout << "--- Operator \"=3D\" called --- " << endl;
_size =3D matrix._size;
}

template <class T>
Matrix<T> Matrix<T>::operator+(const Matrix<T>& second)
{
cout << "--- Operator \"+\" called --- " << endl;
Matrix<T> result(_size);
return result;
}



int main()
{
cout << "Declaration m1" << endl;
Matrix<int> m1(2);
cout << "Declaration m1" << endl;
Matrix<int> m2(2);

cout << "Declaration mSum1" << endl;
Matrix<int> mSum1(2);
cout << "mSum1 =3D m1 + m2" << endl;
mSum1 =3D m1 + m2;

cout << "Declaration mSum2 =3D m1 + m2" << endl;
Matrix<int> mSum2 =3D m1 + m2;

cout << "Declaration mSum3" << endl;
Matrix<int> mSum3(2);
mSum3 =3D m1 + m2;

cout << "Declaration m4 =3D mSum3" << endl;
Matrix<int> m4 =3D mSum3;

int ret;
cin >> ret;

return ret;
}


Comme on voit bien dans le programme en bas la cr=E9ation est appell=E9e
plusieurs fois.

Et voil=E0 la sortie du programme :

$ ./a.exe
Declaration m1
--- Constructor called ---
Declaration m1
--- Constructor called ---
Declaration mSum1
--- Constructor called ---
mSum1 =3D m1 + m2
--- Operator "+" called ---
--- Constructor called ---
--- Operator "=3D" called ---
--- Destructor called ---
Declaration mSum2 =3D m1 + m2
--- Operator "+" called ---
--- Constructor called ---
Declaration mSum3
--- Constructor called ---
--- Operator "+" called ---
--- Constructor called ---
--- Operator "=3D" called ---
--- Destructor called ---
Declaration m4 =3D mSum3
--- Copy constructor called ---
0
--- Destructor called ---
--- Destructor called ---
--- Destructor called ---
--- Destructor called ---
--- Destructor called ---
--- Destructor called ---


Ce que m'int=E9resse le plus c'est pourquoi j'ai ces lignes l=E0 :
Declaration mSum2 =3D m1 + m2
--- Operator "+" called ---
--- Constructor called ---

Je ne vois pas l'operator=3D ni de constructeur de copie. Si je mets le
vrai code pour la classe matrix, le mSum2 est en effet la somme des 2,
l'objet est cr=E9=E9 et vit bien =E7a vie...

Quelqu'un saurait-il m'expliquer la suite des appels ici ? J'imagine
que la ligne
--- Constructor called ---
correspond =E0 la cr=E9ation de l'objet temporaire dans l'operator +. Mais
o=F9 est la trace de l'operator =3D ?



Merci d'avance
A+

10 réponses

1 2 3 4 5
Avatar
Fabien LE LEZ
On Mon, 06 Aug 2007 06:36:52 -0700, Alex Paris :

Matrix<int> mSum2 = m1 + m2;


Prenons un exemple plus simple :

MaClasse mon_objet= x;

Le symbole "=" induit le lecteur novice en erreur.
Malgré les apparences, il n'y a aucune affectation ici, mais une
construction.
En fait, cette ligne est exactement équivalente à :

MaClasse mon_objet (x);
si x est de type MaClasse
OU
MaClasse mon_objet (MaClasse (x));
si x n'est pas de type MaClasse

Note que c'est très différent de

MaClasse mon_objet; // constructeur par défaut
mon_objet= 3; // affectation.


Du coup, la ligne :
Matrix<int> mSum2 = m1 + m2;

est équivalente à
Matrix<int> mSum2 (m1 + m2);

Avatar
Fabien LE LEZ
Je te conseille la lecture de <http://www.gotw.ca/gotw/001.htm>.
D'une manière générale, je conseille fortement la lecture des GotW
<http://www.gotw.ca/gotw/>, et/ou des bouquins "Exceptional C++" et
"More Exceptional C++" de H. Sutter.
Avatar
Jean-Marc Bourguet
Alex Paris writes:

Ce que m'intéresse le plus c'est pourquoi j'ai ces lignes là :
Declaration mSum2 = m1 + m2
--- Operator "+" called ---
--- Constructor called ---

Je ne vois pas l'operator= ni de constructeur de copie. Si je mets le
vrai code pour la classe matrix, le mSum2 est en effet la somme des 2,
l'objet est créé et vit bien ça vie...

Quelqu'un saurait-il m'expliquer la suite des appels ici ? J'imagine
que la ligne
--- Constructor called ---
correspond à la création de l'objet temporaire dans l'operator +. Mais
où est la trace de l'operator = ?


Il y a une optimisation permise permettant la supression du constructeur de
copie dans un certain nombre de cas (voir RVO et NRVO pour les variantes
qui sont vraisemblablement en jeu ici). Le constructeur de copie doit
cependant toujours etre accessible.


A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Michel Decima
Alex Paris writes:

Ce que m'intéresse le plus c'est pourquoi j'ai ces lignes là :
Declaration mSum2 = m1 + m2
--- Operator "+" called ---
--- Constructor called ---

Je ne vois pas l'operator= ni de constructeur de copie. Si je mets le
vrai code pour la classe matrix, le mSum2 est en effet la somme des 2,
l'objet est créé et vit bien ça vie...

Quelqu'un saurait-il m'expliquer la suite des appels ici ? J'imagine
que la ligne
--- Constructor called ---
correspond à la création de l'objet temporaire dans l'operator +. Mais
où est la trace de l'operator = ?


Il y a une optimisation permise permettant la supression du constructeur de
copie dans un certain nombre de cas (voir RVO et NRVO pour les variantes
qui sont vraisemblablement en jeu ici). Le constructeur de copie doit
cependant toujours etre accessible.


Et si on la desactive, on voit bien le comportement attendu : un appel
au constructeur par defaut dans operator+, et deux appels au
constructeur de copie : un pour le temporaire resultat de operator+, et
un pour construire mSum2 (comme l'a dit Fabien, c'est bien le
constructeur de copie qui doit etre appelé, meme si on utilise le
signe =).

g++ -fno-elide-constructors matrix.cpp && ./a.out

Declaration mSum2 = m1 + m2
--- Operator "+" called ---
--- Constructor called ---
--- Copy constructor called ---
--- Destructor called ---
--- Copy constructor called ---
--- Destructor called ---

Remarque: dans le cas present, avec des matrices d'une taille
respectable, l'optimisation peut etre vraiment interessante.


Avatar
Fabien LE LEZ
On Mon, 06 Aug 2007 06:36:52 -0700, Alex Paris :

template<class T>
class Matrix


Ch'tite remarque en passant : dans du code réel, pour une telle
classe, les constructeurs, destructeur et opérateur de copie sont
inutiles. Ou plus exactement, ceux créés automatiquement par le
compilateur conviennent.
C'est d'ailleurs le cas de la majorité des classes ayant une
sémantique de valeur.

Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

On Mon, 06 Aug 2007 06:36:52 -0700, Alex Paris :

template<class T>
class Matrix


Ch'tite remarque en passant : dans du code réel, pour une telle
classe, les constructeurs, destructeur et opérateur de copie sont
inutiles. Ou plus exactement, ceux créés automatiquement par le
compilateur conviennent.
C'est d'ailleurs le cas de la majorité des classes ayant une
sémantique de valeur.


Ah? Tu crois que les vector, les list, les deque, les map, les string, etc
de la SL (qui ont tous une semantique de valeur) peuvent utiliser les
constructeurs de copie et les operateurs d'affectation par defaut?

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Fabien LE LEZ
On 06 Aug 2007 17:48:21 +0200, Jean-Marc Bourguet :

Ah? Tu crois que les vector, les list, les deque, les map, les string, etc
de la SL (qui ont tous une semantique de valeur) peuvent utiliser les
constructeurs de copie et les operateurs d'affectation par defaut?


Bien sûr que non. Mais ce sont des exceptions : ce sont des briques de
base, que tu ne crées d'ailleurs pas toi-même. De plus, tout ça
représente relativement peu de classes.

Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

On 06 Aug 2007 17:48:21 +0200, Jean-Marc Bourguet :

Ah? Tu crois que les vector, les list, les deque, les map, les string, etc
de la SL (qui ont tous une semantique de valeur) peuvent utiliser les
constructeurs de copie et les operateurs d'affectation par defaut?


Bien sûr que non. Mais ce sont des exceptions : ce sont des briques de
base, que tu ne crées d'ailleurs pas toi-même. De plus, tout ça
représente relativement peu de classes.


J'ecris peu de classes ayant une semantique de valeur. Sans faire de stat
precises, j'ai du mal a savoir si la plupart d'entre elles ont besoin d'un
operateur d'affectation et d'un constructeur de copie ou pas. En tout cas,
la predominance de celles qui n'en n'ont pas besoin n'est pas si nette et
les autres ne sont pas des exceptions.

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Alex Paris
On 6 août, 16:24, Michel Decima wrote:

Et si on la desactive, on voit bien le comportement attendu : un appel
au constructeur par defaut dans operator+, et deux appels au
constructeur de copie : un pour le temporaire resultat de operator+, et
un pour construire mSum2 (comme l'a dit Fabien, c'est bien le
constructeur de copie qui doit etre appelé, meme si on utilise le
signe =).

g++ -fno-elide-constructors matrix.cpp && ./a.out

Declaration mSum2 = m1 + m2
--- Operator "+" called ---
--- Constructor called ---
--- Copy constructor called ---
--- Destructor called ---
--- Copy constructor called ---
--- Destructor called ---

Remarque: dans le cas present, avec des matrices d'une taille
respectable, l'optimisation peut etre vraiment interessante.


En effet c'est à ça que je pensais... Qu'il y a une optimisation
mystérieuse dans la manière de construire l'objet. Compte tenu du fait
qu'à l'intérieur de "+" il y a une construction qu'on voit bien, mais
la création de l'objet sum2 est assez obscure alors que le nombre de
construction/destruction correspond bien et il n'y a pas de fuite de
mémoire.
Après oui je pensais à la copie bit-wize, vu que de toutes manières la
classe temporaire est à détruire de suite le compilo le garde et le
"renomme" en sum2.

C'est bien connaître par coeur les flags de g++ et leurs
significations : ) Bravo

Avatar
Alex Paris
On 6 août, 16:56, Fabien LE LEZ wrote:
On Mon, 06 Aug 2007 06:36:52 -0700, Alex Paris :

template<class T>
class Matrix


Ch'tite remarque en passant : dans du code réel, pour une telle
classe, les constructeurs, destructeur et opérateur de copie sont
inutiles. Ou plus exactement, ceux créés automatiquement par le
compilateur conviennent.
C'est d'ailleurs le cas de la majorité des classes ayant une
sémantique de valeur.


Non mais en réalité l'exemple est super simplifié. Il y a une donnée
_data qui contient tout la structure... le tableu en fait. Si c'est
déclaré T** je ne pense pas que le compilo sera capable de tout
recopier correctement. La taille est stokée dans le _size. Ca peut
être une matrice m, n etc. Plein choses. Je veux peut-être optimiser
moi-même la mémoire...Enfin c'est utile je pense. Ai-je tort ?


1 2 3 4 5