Mémoire
Déplacement d’objet avec std::move
struct A {
// Constructeur
A() { /* ... */ }
// Constructeur de recopie
A(const A &s) { /* ... */ }
// Opérateur d'assignation
A& operator=(const A& s) { /* ... */ }
// Destructeur
~A() { /* ... */ }
};
#include <utility> // pour std::move(/* ... */);
int main(void) {
vector<std::string> v;
std::string s;
while(s != "END") {
std::cin >> s;
// v.push_back(s);
v.push_back(std::move(s));
}
}
On peut utiliser move
!
Autre exemple :
std::string a = "Bonjour";
std::string b = "Machin";
std::string c = "truc";
a = b;
// a contient "Machin"
// b contient "Machin"
c = std::move(b);
// c contient "Machin"
// b contient la chaîne vide
std::move
a un interêt quand il faut déplacer une ressource sans tout copier.
Pour pouvoir l’utiliser dans notre classe, il faut implanter un autre constructeur de recopie ainsi qu’un autre constructeur d’assignation : (appellé respectivement constructeur de déplacement et l’opérateur de déplacement)
A(const A&& s) { /* ... */}
A& operator=(const A&& s) { /* ... */}
(on double la référence)
Conclusion
Afin de pouvoir optimiser certaines instructions sur une classe A. On peut implanter le constructeur de déplacement (A(const A&&)
) et l’assignation par déplacement (A& operator=(const A&&)
) que l’on utilisera avec std::move()
de <utility>
.
Exemple d’implantation
On suppose que
- on a un pointeur vers unint
qui va pointer vers une zone mémoire que l’on va allouer dynamiquement)
- on a une classeMere
déjà définit
#include <utility>
using namespace std;
struct Fille: public Mere {
int * p = NULL;
// Constructeur qui ne fait rien
Fille(void) { }
// Constructeur qui alloue de la mémoire
Fille(int a): Mere(a) {
p = malloc(sizeof(*p) * 100); // il va falloir free cette allocation
// lors de la déstruction
for(int i = 0; i < 100; i++) {
p[i] = i;
}
}
// Destructeur qui désalloue la mémoire alloué lors de la construction
~Fille(): ~Mere() {
if(p != NULL) {
free(p);
}
}
// Constructeur de copie
Fille(const Fille& s): Mere(s) {
if(s.p != NULL) { // Dans ce cas là on fait une allocation
p = malloc(sizeof(*p) * 100);
for(int i = 0; i < 100; i++) {
p[i] = s.p[i];
}
}
}
// Affectation/Assignation
Fille& operator=(const Fille& s) {
(*this).Mere::operator(s); // on appelle celui de la classe Mere
if(s.p == NULL) { // si s.p est NULL
if(p != NULL) { // mais que p l'est pas, on libère p
free(p);
p = NULL;
}
} else { // sinon si s.p n'est pas NULL
if(p == NULL) { // on alloue la mémoire si besoin
p = malloc(sizeof(*p) * 100);
}
for(int i = 0; i < 100; i++) { // on recopie les valeurs
p[i] = s.p[i];
}
}
return *this;
}
// Constructeur de déplacement
Fille(const Fille&& s): Mere(move(s)) {
p = s.p; // déplacement
s.p = NULL; // ancienne emplacement est désormais NULL
}
// Affectation de déplacement
Fille& operator=(const Fille&& s) {
// Partie mère
(*this).Mere::operator=(move(s));
// Partie Fille
if(p != NULL) {
free(p);
}
p = s.p;
s.p = NULL;
return *this;
}
};
Appel du constructeur d’une classe sans réservation de mémoire
Allocation de mémoire, puis appel du constructeur d’une classe A :
A a;
A * p = new A;
Pour appeller un constructeur d’une classe A sur une adresse addr
(sans allocation de mémoire) on peut utiliser la syntaxe :
new(addr) A{ /* ... */ }; // appel du constructeur `A` à l'adresse `addr`
Exemple
void * ptr = malloc(sizeof(*ptr)); // réservation de la mémoire
A * c = new(ptr) A{ /* ... */ }; // appel au constructeur sans réservation mémoire
// ...
(*c).~A(); // appel au destructeur sans libération de la mémoire alloué plus haut
free(ptr);
Utile lorsque l’on ne veut réallouer plusieurs fois de la mémoire.
Cast dynamique
En C++, il y a plusieurs moyen de faire des conversions.
En C : (type)expression;
En C++ :
const_cast<type>(expression);
reinterpret_cast<type>(expression);
static_cast<type>(expression);
dynamic_cast<type>(expression);
- Va servir lorsque l’on a une adresse vers une classe mère mais on ne sait pas si c’est l’objet mère ou un objet fille.
Exemple
dynamic_cast<>()
Mere * p = new Fille( /* ... */);
Fille * c = dynamic_cast<Fille>(p); // au moment de l'execution, NULL si ce n'est
// pas une fille ou un type hérité de fille
if(c == NULL) {
cout << "N'est pas une fille" << endl;
} else {
cout << "Est une fille" << endl;
}
// Ce code affichera toujours "Est une fille"