OVH Cloud OVH Cloud

cas d'utilisation du constructeur de copie

26 réponses
Avatar
Nicolas Aunai
bonjour,


Quels sont les différents cas dans lequel le c'est le constructeur de
copie de la classe qui est appelé ?

mon cours me dit :

ma_classe c(c1); // instanciation de c a partir de c1

ainsi que

ma_classe c=c1;


mais est- il valide de faire :

ma_classe c;
ma_classe c1;

et plus loin...

c=c1; ? le constructeur par copie est-il appelé ?

que se passe-t-il ? étant donnné que le constructeur a déja été appelé
lors de la déclaration de c....

merci

--
Nico,
http://astrosurf.com/nicoastro
messenger : nicolas_aunai@hotmail.com

10 réponses

1 2 3
Avatar
Serge Paccalin
Le samedi 11 octobre 2003 à 20:34:56, Alexandre a écrit dans
fr.comp.lang.c++ :

Quand on a un pointeur sur un objet valide, on peut (je crois) appeler
le destructeur, puis le constructeur. Mais bon, c'est de la sale
bidouille -- je préfère ne jamais essayer ;-)

Comme ça ?


OBJET *ptr;
ptr = new OBJET ; // appel du constructeur
delete ptr; // appel du destructeur
ptr = new OBJET; // appel du constructeur


Non, comme ça :

OBJET *ptr;
ptr = new OBJET ; // appel du constructeur
ptr->~OBJET(); // appel du destructeur
ptr->OBJET(); // appel du constructeur

--
___________ 2003-10-11 20:48:50
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763


Avatar
Serge Paccalin
Le samedi 11 octobre 2003 à 20:34:56, Alexandre a écrit dans
fr.comp.lang.c++ :

Quand on a un pointeur sur un objet valide, on peut (je crois) appeler
le destructeur, puis le constructeur. Mais bon, c'est de la sale
bidouille -- je préfère ne jamais essayer ;-)

Comme ça ?


OBJET *ptr;
ptr = new OBJET ; // appel du constructeur
delete ptr; // appel du destructeur
ptr = new OBJET; // appel du constructeur


Non, comme ça :

OBJET *ptr;
ptr = new OBJET ; // allocation+appel du constructeur
ptr->~OBJET(); // appel du destructeur
ptr->OBJET(); // appel du constructeur

--
___________ 2003-10-11 20:48:50
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763


Avatar
Serge Paccalin
Le samedi 11 octobre 2003 à 20:34:56, Alexandre a écrit dans
fr.comp.lang.c++ :

Quand on a un pointeur sur un objet valide, on peut (je crois) appeler
le destructeur, puis le constructeur. Mais bon, c'est de la sale
bidouille -- je préfère ne jamais essayer ;-)

Comme ça ?


OBJET *ptr;
ptr = new OBJET ; // appel du constructeur
delete ptr; // appel du destructeur
ptr = new OBJET; // appel du constructeur


Non, plutôt comme ça :

OBJET *ptr;
ptr = new OBJET ; // allocation+appel du constructeur
ptr->~OBJET(); // appel du destructeur
ptr->OBJET(); // appel du constructeur

Mais ça ne doit pas marcher. Il faut sans doute utiliser un « placement
new » : new(ptr) OBJET;

--
___________ 2003-10-11 20:48:50
_/ _ _`_`_`_) Serge PACCALIN -- sp ad mailclub.net
_L_) Il faut donc que les hommes commencent
-'(__) par n'être pas fanatiques pour mériter
_/___(_) la tolérance. -- Voltaire, 1763


Avatar
Fabien LE LEZ
On Sat, 11 Oct 2003 20:34:56 +0200, "Alexandre"
wrote:

Comme ça ?


Le seul cas où je l'aie vu, c'est :

Objet& operator = (Objet& o)
{
this->~Objet();
this->Objet(o);
return *this;
}

Mais encore une fois, c'est le genre de construction à ne jamais
utiliser.

--
http://www.giromini.org/usenet-fr/repondre.html

Avatar
Loïc Joly
Fabien LE LEZ wrote:

On Sat, 11 Oct 2003 17:20:33 +0200, "Spoofix"
<spoofix[ENLEVEZ_CA]@free.fr> wrote:


MyClass myCl(5);

// est-il possible de faire :
myCl.MyClass(6);



Non, si c'est ce que tu veux, tu peux soit :

Mettre dans MyClass une fnction init qui sera appelée par le
constructeur ou explicitement.

Mettre dans MyClass un opérateur = et appeler :
myCl = MyClass(6);

--
Loïc


Avatar
Loïc Joly
Fabien LE LEZ wrote:

On Sat, 11 Oct 2003 16:10:21 +0200, Nicolas Aunai
ç wrote:


ma_classe c(c1); // instanciation de c a partir de c1



ma_classe cÁ;



Sauf si le constructeur est explicite, auquel cas, seule la première
écriture est valide.

--
Loïc


Avatar
James Kanze
Serge Paccalin writes:

|> Le samedi 11 octobre 2003 à 20:34:56, Alexandre a écrit dans
|> fr.comp.lang.c++ :

|> >> Quand on a un pointeur sur un objet valide, on peut (je crois)
|> >> appeler le destructeur, puis le constructeur. Mais bon, c'est de
|> >> la sale bidouille -- je préfère ne jamais essayer ;-)

|> > Comme ça ?

|> > OBJET *ptr;
|> > ptr = new OBJET ; // appel du constructeur
|> > delete ptr; // appel du destructeur
|> > ptr = new OBJET; // appel du constructeur

|> Non, plutôt comme ça :

|> OBJET *ptr;
|> ptr = new OBJET ; // allocation+appel du constructeur
|> ptr->~OBJET(); // appel du destructeur
|> ptr->OBJET(); // appel du constructeur

|> Mais ça ne doit pas marcher. Il faut sans doute utiliser un
|> « placement new » : new(ptr) OBJET;

En effet. En fait, il est impossible à « appeler » le
constructeur ; il est toujours appelé implicitement, lors de
l'initialisation dans une définition, lors d'une expression de new,
ou lors d'une conversion de type. En revanche, ce dernier se ressemble
beaucoup à un appel explicit, surtout quand le constructeur n'a pas
de paramètres ou qu'il en a plus d'un. N'empêche que le
résultat, c'est toujours un nouvel objet, temporaire.

Fabien s'est trompé aussi en disant qu'il faut absolument appeler le
destructeur d'abord. Si le destructeur est trivial, on n'est pas
obligé à l'appeler, et quelque chose comme :

std::complex< double > z ;
// ...
new ( &z ) std::complex< double >( 1.0, 2.0 ) ;

est parfaitement légal (mais totalement sans intérêt).

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Avatar
James Kanze
Nicolas Aunai ç writes:

|> "Fabien LE LEZ" avait prétendu :

|> >> ma_classe c(c1); // instanciation de c a partir de c1

|> >> ma_classe cÁ;

|> > A noter que si c1 est du même type que c, alors les deux
|> > écritures sont équivalentes.

|> ok, alors si j'avais eu par exemple :

|> class Entier
|> {
|> int i ;
|> public :
|> Entier(int j)
|> {
|> i=j ;
|> return ;
|> }
|> } ;

|> et ensuite :

|> int j=2 ;
|> Entier e1, e2=j ;

Erreur de compilation pour e1 ; Entier n'a pas de constructeur par
défaut.

En ce qui concerne e2, formellement, le compilatuer « convertit »
l'expression « j » en Entier, au moyen de Entier(int), puis
appelle le constructeur de copie (ici, fournit implicitement) pour
copier la valeur dans e2. La norme donne explicitement au compilateur le
droit de supprimer la copie, en construisant directement dans e2 à
partir de j, mais seulement à condition que le programme soit
légal si le constructeur de copie avait réelement servi.

|> e1=j ;

Ici, il n'y a pas d'appel du constructeur, mais seulement de
l'opérateur d'affectation (aussi fourni explicitement par le
compilateur).

Pour que ce qui se passe soit plus clair, je suggèrerais d'écrire
la classe avec tous les constructeurs explicits :

class Entier
{
public:
Entier() : i( 0 ), id( ++ instanceId ) {
std::cout << id << " : Constructeur par défaut appelén" ;
}
Entier( int j ) : i( j ), id( ++ instanceId ) {
std::cout << id << " : Constructeur avec int appelén" ;
}
Entier( Entier const& other ) : i( other.i ), id( ++ instanceId ) {
std::cout << id << " : Constructeur de copie appelén" ;
}
~Entier() {
std::cout << id << " : Destructeur appelén" ;
}
Entier& operator=( Entier const& other ) {
i = other.i ;
std::cout << id << " : Opérateur d'affectation appelén" ;
return *this ;
}
private:
int i ;
int id ;
static int instanceId ;
} ;
int Entier::instanceId = 0 ;

Ça vaut la peine d'expérimenter aussi avec de différents
constructeurs déclarés privés -- comme j'ai dit ci-dessus, il y
a des cas où le compilateur est obligé à pouvoir appeler un
constructeur, sans qu'il soit obligé à l'appeler réelement.

Tu pourrais aussi expériementer avec d'autres cas où le
constructeur et/ou l'opérateur d'affectation est appelé : avec par
exemple une fonction qui prend un Entier en paramètre, ou qui renvoie
un Entier. Quelque chose du genre :

Entier
f( Entier i )
{
std::cout << "Dans fn" ;
return i ;
}

int
main()
{
Entier i ;
Entier j = f( i ) ;
i = f( j ) ;
return 0 ;
}

par exemple.

Toutefois, ne perd pas de vue que si de telles expériences peuvent
aider à la compréhension, elles ne révèlent jamais que ce
que fait un compilateur donné. À la fin, il faut bien vérifier
les règles (et éventuellement essayer avec d'autres compilateurs),
pour savoir quelles sont les libertés qu'a le compilateur.

--
James Kanze mailto:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Avatar
Gabriel Dos Reis
James Kanze writes:

[...]

| Fabien s'est trompé aussi en disant qu'il faut absolument appeler le
| destructeur d'abord. Si le destructeur est trivial, on n'est pas
| obligé à l'appeler, et quelque chose comme :
|
| std::complex< double > z ;
| // ...
| new ( &z ) std::complex< double >( 1.0, 2.0 ) ;
|
| est parfaitement légal (mais totalement sans intérêt).

La norme ne décrit pas std::complex<double> comme ayant un destructeur
trivial.

-- Gaby
Avatar
Alexandre
Objet& operator = (Objet& o)
{
this->~Objet();
this->Objet(o); ?????
return *this;
}
C'est légal, ça ?



Mais encore une fois, c'est le genre de construction à ne jamais
utiliser.

--
http://www.giromini.org/usenet-fr/repondre.html


1 2 3