Compilation & Directives de préprocesseur Pointeurs, Références & Allocation Dynamique de Mémoire Spécificités du C++ (indépendantes de la POO) Structures & Classes Encapsulation des données Constructeur & Destructeur de classe Copie d'objets Amitié & Surcharge d'opérateur Héritage (Partie 1) Héritage (Partie 2) Patrons de fonctions et de classes Introduction à la librairie standard
Retour menu principal

Copie d’objets


Rappel : passage par valeur, par référence

Passage par valeur

void f(int i)
{
  i = 10;
  cout << "f : " << i << endl;
}

int main()
{
  int e = 0;
  f(e);
  cout << "main : " << e << endl;
  return EXIT_SUCCESS;
}

f : 10
main : 0

Passage par référence

void g(int& i)
{
  i = 10;
  cout << "g : " << i << endl;
}

int main()
{
  int e = 0;
  g(e);
  cout << "main : " << e << endl;
  return EXIT_SUCCESS;
}

g : 10
main : 10

Passage d’objets par valeur, par référence

Passage par valeur

void f(Complexe z)
{
  z.set_real_imag(2, 3);
  cout << "f : ";
  z.affiche();
}

int main()
{
  Complexe p(1, 0.5);
  f(p);
  cout << "main : ";
  p.affiche();
  return EXIT_SUCCESS;
}

f : (2 + 3i)
main : (1 + 0.5i)

Passage par référence

void g(Complexe& z)
{
  z.set_real_imag(2, 3);
  cout << "g : ";
  z.affiche();
}

int main()
{
  Complexe p(1, 0.5);
  g(p);
  cout << "main : ";
  p.affiche();
  return EXIT_SUCCESS;
}

g : (2 + 3i)
main : (2 + 3i)

Passage d’objets par valeur, par référence

Passage par valeur

void f(Complexe z)
{
  cout << "Le complexe vaut ";
  z.affiche();
}

int main()
{
  Complexe p(1, 0.5);
  f(p);
  return EXIT_SUCCESS;
}

L’objet est copié

Passage par référence constante

void g(const Complexe& z)
{
  cout << "Le complexe vaut ";
  z.affiche();
}

int main()
{
  Complexe p(1, 0.5);
  g(p);
  return EXIT_SUCCESS;
}

Une copie inutile est évitée

Objets constants

Le mot-clé const interdit la modification de l’objet auquel il s’applique.

const int i = 42;
i = 3;  // erreur de compilation
void f(const Complexe& z)
{
  z.affiche();  // possible
  z.set_real_imag(1, 2);  // impossible
}

Le compilateur doit savoir quelles méthodes modifient l’objet.

Méthodes et “const-correctness”

Le prototype d’une méthode doit se terminer par const si celle-ci ne modifie pas l’objet :

class Complexe
{
public:
  ...
  void affiche() const;
  void set_real_imag(double real, double imag);
  ...
};
  • Par défaut les méthodes peuvent modifier l’objet.
  • En cas d’oubli de const, la méthode ne peut donc pas être utilisée sur des objets constants.

Copier un objet

La copie est la création d’un nouvel objet ayant la même valeur qu’un objet existant.

class Point
{
public:
  ...
  Point(const Point& other);
  ...
private:
  int m_x;
  int m_y;
};

Copier un objet : le constructeur de copie

Exemple de définition d’un constructeur de copie :

#include "Point.h"
#include <iostream>
using namespace std;

Point::Point(const Point& other)
  : m_x(other.m_x), m_y(other.m_y)
{
  cout << "copie de Point" << endl;
}
  • En l’absence de constructeur de copie le compilateur en génère un automatiquement : il copie une à une les valeurs des membres.
  • Lorsqu’une classe gère de la mémoire allouée dynamiquement, un constructeur de copie est donc indispensable.

Exemple de copie inévitable

Parfois on ne peut pas éviter de copier un objet :

Complexe f();

int main()
{
  Complexe z = f();
  z.affiche();
  return EXIT_SUCCESS;
}

La valeur de retour de f est copiée dans le nouvel objet z.

Exemple de copie évitable

Mais on peut souvent éviter des copies inutiles :

Copie inutile

Complexe f()
{
  Complexe z(1, 2);
  return z;
}

La variable locale z est copiée dans un objet anonyme retourné par la fonction.

Sans copie inutile

Complexe f()
{
  return Complexe(1, 2);
}

Il n’y a plus de variable locale. L’objet anonyme est construit directement.