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.

#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 cela n’est pas nécessaire lors d’appel et retour de fonctions. 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 normalement :

  • 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(...).

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

Exercice

  • 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 là ?
#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.