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 objetstringque 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);
}
- 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());
}
- 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());
}