Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les classes C++ sont, par défaut, des types valeur. Ils peuvent être spécifiés en tant que types de référence, ce qui permet au comportement polymorphe de prendre en charge la programmation orientée objet. Les types valeur sont parfois consultés du point de vue de la mémoire et du contrôle de disposition, tandis que les types de référence concernent les classes de base et les fonctions virtuelles à des fins polymorphes. Par défaut, les types valeur peuvent être copiés, ce qui signifie qu’il existe toujours un constructeur de copie et un opérateur d’affectation de copie. Pour les types de référence, vous créez la classe non copieable (désactivez le constructeur de copie et l’opérateur d’affectation de copie) et utilisez un destructeur virtuel, qui prend en charge leur polymorphisme prévu. Les types valeur concernent également le contenu, qui, lorsqu’ils sont copiés, vous donnent toujours deux valeurs indépendantes qui peuvent être modifiées séparément. Les types de référence concernent l’identité : quel type d’objet est-il ? Pour cette raison, les « types de référence » sont également appelés « types polymorphes ».
Si vous souhaitez vraiment un type de référence (classe de base, fonctions virtuelles), vous devez désactiver explicitement la copie, comme indiqué dans la classe dans le MyRefType
code suivant.
// cl /EHsc /nologo /W4
class MyRefType {
private:
MyRefType & operator=(const MyRefType &);
MyRefType(const MyRefType &);
public:
MyRefType () {}
};
int main()
{
MyRefType Data1, Data2;
// ...
Data1 = Data2;
}
La compilation du code ci-dessus entraîne l’erreur suivante :
test.cpp(15) : error C2248: 'MyRefType::operator =' : cannot access private member declared in class 'MyRefType'
meow.cpp(5) : see declaration of 'MyRefType::operator ='
meow.cpp(3) : see declaration of 'MyRefType'
Types de valeurs et efficacité de déplacement
La surcharge d’allocation de copie est évitée en raison de nouvelles optimisations de copie. Par exemple, lorsque vous insérez une chaîne au milieu d’un vecteur de chaînes, il n’y a pas de surcharge de réallocation de copie, seulement un déplacement, même s’il entraîne une croissance du vecteur lui-même. Ces optimisations s’appliquent également à d’autres opérations : par exemple, en effectuant une opération d’ajout sur deux objets énormes. Comment activer ces optimisations de l’opération de valeur ? Le compilateur les active implicitement, tout comme les constructeurs de copie, peuvent être générés automatiquement par le compilateur. Toutefois, votre classe doit « accepter » pour déplacer des devoirs et des constructeurs de déplacement en les déclarant dans votre définition de classe. Move utilise la référence rvalue (> double ampersand) dans les déclarations de fonction membre appropriées et définit les méthodes de constructeur de déplacement et d’affectation de déplacement. Vous devez également insérer le code approprié pour « voler les intestins » de l’objet source.
Comment décider si vous avez besoin d’activer les opérations de déplacement ? Si vous savez déjà que vous avez besoin d’activer la construction de copie, vous souhaitez probablement activer la construction de déplacement, en particulier s’il est moins cher qu’une copie approfondie. Toutefois, si vous savez que vous avez besoin d’une prise en charge de déplacement, cela ne signifie pas nécessairement que vous souhaitez activer les opérations de copie. Ce dernier cas serait appelé « type move-only ». Un exemple déjà dans la bibliothèque standard est unique_ptr
. En guise de note latérale, l’ancien auto_ptr
est déconseillé et a été remplacé unique_ptr
par précisément en raison de l’absence de prise en charge sémantique de déplacement dans la version précédente de C++.
En utilisant la sémantique de déplacement, vous pouvez retourner par valeur ou insérer au milieu. Le déplacement est une optimisation de la copie. Il n’est pas nécessaire d’allouer un tas comme solution de contournement. Considérez le pseudocode suivant :
#include <set>
#include <vector>
#include <string>
using namespace std;
//...
set<widget> LoadHugeData() {
set<widget> ret;
// ... load data from disk and populate ret
return ret;
}
//...
widgets = LoadHugeData(); // efficient, no deep copy
vector<string> v = IfIHadAMillionStrings();
v.insert( begin(v)+v.size()/2, "scott" ); // efficient, no deep copy-shuffle
v.insert( begin(v)+v.size()/2, "Andrei" ); // (just 1M ptr/len assignments)
//...
HugeMatrix operator+(const HugeMatrix& , const HugeMatrix& );
HugeMatrix operator+(const HugeMatrix& , HugeMatrix&&);
HugeMatrix operator+( HugeMatrix&&, const HugeMatrix& );
HugeMatrix operator+( HugeMatrix&&, HugeMatrix&&);
//...
hm5 = hm1+hm2+hm3+hm4+hm5; // efficient, no extra copies
Activation du déplacement pour les types de valeurs appropriés
Pour une classe de type valeur où le déplacement peut être moins cher qu’une copie approfondie, activez la construction de déplacement et l’affectation de déplacement pour l’efficacité. Considérez le pseudocode suivant :
#include <memory>
#include <stdexcept>
using namespace std;
// ...
class my_class {
unique_ptr<BigHugeData> data;
public:
my_class( my_class&& other ) // move construction
: data( move( other.data ) ) { }
my_class& operator=( my_class&& other ) // move assignment
{ data = move( other.data ); return *this; }
// ...
void method() { // check (if appropriate)
if( !data )
throw std::runtime_error("RUNTIME ERROR: Insufficient resources!");
}
};
Si vous activez la construction/affectation de copie, activez également la construction/l’affectation de déplacement si elle peut être moins coûteuse qu’une copie approfondie.
Certains types non-valeur sont déplacés uniquement, par exemple lorsque vous ne pouvez pas cloner une ressource, transférer uniquement la propriété. Exemple : unique_ptr
.
Voir aussi
Système de type C++
Bienvenue dans C++
Informations de référence sur le langage C++
Bibliothèque C++ standard