Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Eviter des copies

A la construction d’un objet

A la construction d’un objet, les champs s’initialisent tout seul avant le constructeur. Quand on ne dit sur la création d’un objet, C++ appelle le constructeur par défaut qui n’a pas de paramètres.

#include <iostream>
#include <vector>

class Point {
    public:
        int x;
        int y;
        
        Point() {
            std::cout << "Creation par defaut d'un point\n";
            x = 0; y = 0; 
        }
        
        Point(int ax, int ay) {
            x = ax; y = ay;
            std::cout << "Creation d'un point\n";
        }
        
        
};

class Animal {
    public:
        Point pos; // malheureusement est initialisé...
        Animal(Point apos) {
            pos = apos;
        };
};


int main()
{
    Animal a(Point(5, 2));
    std::cout << a.pos.x;
    return 0;
}

Pour éviter un appel au constructeur par défaut et appeler uniquement les constructeurs pour les champs au même moment du constructeur de l’animal, on utilise une liste d’initialisation.

#include <iostream>
#include <vector>

class Point {
    public:
        int x;
        int y;
        
        Point() {
            std::cout << "Creation par defaut d'un point\n";
            x = 0; y = 0; 
        }
        
        Point(int ax, int ay) {
            x = ax; y = ay;
            std::cout << "Creation d'un point\n";
        }
        
        
};

class Animal {
    public:
        Point pos; 
        Animal(Point apos) pos(apos) {}; // pos n'est plus initialement avec le constructeur par défaut car il apparaît dans la liste d'initialisation
};


int main()
{
    Animal a(Point(5, 2));
    std::cout << a.pos.x;
    return 0;
}

Et puis tant qu’à faire, autant avoir des listes d’initialisation partout.

#include <iostream>
#include <vector>

class Point {
    public:
        int x;
        int y;
        
        Point() : x(0), y(0) {
            std::cout << "Creation par defaut d'un point\n";
        }
        
        Point(int ax, int ay) : x(ax), y(ay) {
            std::cout << "Creation d'un point\n";
        }   
};

class Animal {
    public:
        Point pos; 
        Animal(Point apos) : pos(apos) {}; // pos n'est plus initialement avec le constructeur par défaut car il apparaît dans la liste d'initialisation
};

int main()
{
    Animal a(Point(5, 2));
    std::cout << a.pos.x;
    return 0;
}

Élision de copie

Une élision de copie est une omission de copie quand C++ sait qu’elle n’est pas nécessaire : typiquement lors d’un appel ou au retour d’une fonction. On évite d’appeler un constructeur de copie inutilement.

Exemple

Considérons ce morceau de code.

V.push_back(getStringFromClient())

Voici ce qui se passera sans élision de copie :

  • getStringFromClient() renvoie un objet string que l’on note temporairement A (attention, il n’y a pas de variable A dans le programme, ce n’est donc pas une l-value)
  • On crée une copie A’ que l’on donne à push_back
  • Puis A est supprimé

Mais il y a trop de copie. Au lieu de cela, les copies sont évitées, et les données sont juste transférées de getStringFromClient à l’intérieur de V.push_back(...).

Attention, le compilateur fait une sorte de move semantics automatique dans ce cas là. On appelle ça une élision de copie.

Exercices

  • Voici un programme. Que fait-il ?
#include<iostream>

class Cat {
    public:
        Cat() {
            std::cout << "By default constructor\n";
        }
        
        Cat(bool is_magical) {
            std::cout << "Constructor\n";
        }
        
        Cat(Cat& cat) {
            std::cout << "Constructor by copy\n";
        }
        
        Cat(Cat&& cat) {
            std::cout << "Constructor by move\n";
        }
    
};


Cat getSuperCat() {
    return Cat();
}

void teachSth(Cat c) {}

int main()
{
    Cat c;
    teachSth(c);
}

Il appelle le constructeur par défaut, puis le constructeur par copie.

Voici un programme. Que fait-il ?

#include<iostream>

class Cat {
    public:
        Cat() {
            std::cout << "By default constructor\n";
        }
        
        Cat(bool is_magical) {
            std::cout << "Constructor\n";
        }
        
        Cat(Cat& cat) {
            std::cout << "Constructor by copy\n";
        }
        
        Cat(Cat&& cat) {
            std::cout << "Constructor by move\n";
        }
    
};


Cat getSuperCat() {
    return Cat();
}

void teachSth(Cat c) {}

int main()
{
    teachSth(getSuperCat());
}

Il appelle le constructeur par défaut.

Et que fait ce programme ?

#include<iostream>

class Cat {
    public:
        Cat() {
            std::cout << "By default constructor\n";
        }
        
        Cat(bool is_magical) {
            std::cout << "Constructor\n";
        }
        
        Cat(Cat& cat) {
            std::cout << "Constructor by copy\n";
        }
        
        Cat(Cat&& cat) {
            std::cout << "Constructor by move\n";
        }
    
};


Cat getSuperCat() {
    Cat c;
    return c;
}

void teachSth(Cat c) {}

int main()
{
    teachSth(getSuperCat());
}

Il appelle le constructeur par défaut.