Probleme de comprehension : constructeur de copie (templates?)

Le
Alex Paris
Bonjour à tous !

Voilà 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=(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=(const Matrix<T>& matrix)
{
cout << " Operator "=" called " << endl;
_size = 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 = m1 + m2" << endl;
mSum1 = m1 + m2;

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

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

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

int ret;
cin >> ret;

return ret;
}


Comme on voit bien dans le programme en bas la création est appellée
plusieurs fois.

Et voilà la sortie du programme :

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


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 = ?



Merci d'avance
A+
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 6
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Fabien LE LEZ
Le #310206
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);

Fabien LE LEZ
Le #310205
Je te conseille la lecture de D'une manière générale, je conseille fortement la lecture des GotW
"More Exceptional C++" de H. Sutter.
Jean-Marc Bourguet
Le #310204
Alex Paris
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

Michel Decima
Le #310203
Alex Paris
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.


Fabien LE LEZ
Le #310202
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.

Jean-Marc Bourguet
Le #310200
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.


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


Fabien LE LEZ
Le #310199
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.

Jean-Marc Bourguet
Le #310198
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.


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


Alex Paris
Le #310163
On 6 août, 16:24, Michel Decima
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

Alex Paris
Le #310162
On 6 août, 16:56, 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.


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 ?


Publicité
Poster une réponse
Anonyme