Overblog Suivre ce blog
Editer l'article Administration Créer mon blog
16 octobre 2010 6 16 /10 /octobre /2010 14:06

 

J'ai brièvement expliqué dans mon précédent billet l'intérêt des rvalue references de C++11 et nous avons pu voir que les améliorations de performances dans les programmes génériques justifiaient amplement leur inclusion dans la nouvelle norme. Néanmoins, je tiens à rappeler que cet ajout se fera aux dépens d'une complexité accrue du langage. Le programmeur débutant devra maitriser les concepts de déplacement et de copie, ainsi que les temporaires anonymes, avant de pouvoir tirer avantage de cette fonctionnalité.

Le langage D se voulant être un C++ plus simple, il se devait de résoudre ce problème de manière plus élégante.

Classes et structures

 

Premier constat, une classe D n'a pas grand-chose en commun avec son homologue C++. D a choisi la même approche que C#, en séparant clairement les structures et les classes, alors que ces deux types correspondent plus ou moins à la même chose en C++.

En D, une classe est toujours instanciée sur le tas, au moyen de l'opérateur new et est toujours manipulée par référence. De cette manière, aucune copie accidentelle d'objet classe n'aura lieu tant que le programmeur l'aura expressément demandé (appel à une méthode clone, par exemple). Il n'est pas possible de surcharger l'opérateur d'affectation pour les classes.

 

Mais D propose aussi un type structure, qui est proche des structures/classes C++ et se manipule aussi par valeur ; avec quelques différences cependant. Les structures D ont notamment une contrainte supplémentaire : elles doivent être déplaçables. C'est-à-dire que l'état de l'objet doit être indépendant de sa position en mémoire. En pratique, cela signifie qu'une structure ne peut contenir de pointeur vers ses propres membres. Le code suivant est donc incorrect en D :

 struct S
{
private:
int i;
int* pi;

public:
this(int value)
{
i = value;
pi = &i;
}
}

 

En pratique, cela ne pose pas de problème, car cette technique n'est jamais indispensable. Le fait d'avoir des objets déplaçables présente des avantages d'optimisation pour le compilateur ou l'environnement d'exécution, comme le ramasse-miettes.

Le constructeur postblit

 

Contrairement au langage C++, le langage D ne propose pas de constructeur de copie pour les structures. Le constructeur de copie est un mécanisme du C++ dans lequel une méthode spécifique est appelée lorsqu'un objet doit être copié. Si le programmeur ne définit par de constructeur de copie, le compilateur C++ en générera un automatiquement. Le constructeur de copie généré se bornera à faire une copie brute (memcpy) des données de l'objet. Cela peut suffire pour les types simples (appelés POD, pour Plain Old Data), mais est clairement insuffisant pour un objet complexe, possédant des pointeurs vers des données externes. C'est précisément la raison pour laquelle C++ permet au programmeur d'écrire son propre constructeur de copie.

 

Comme je l'ai dit, le langage D ne propose pas de constructeur de copie. En D, toutes les copies de structures seront toujours assurées par le langage, qui fera toujours un memcpy des données de la structure. Comme cette technique est insuffisante pour les types complexes, D propose un autre mécanisme appelé le constructeur postblit. C'est en fait une méthode optionnelle qui sera appelée après une copie de structure. J'insiste bien sur le fait que ce n'est pas un constructeur de copie car il ne remplace pas le mécanisme de copie de la structure, il est appelé après la copie brute des champs de la structure.

Dans un constructeur postblit, le programmeur insèrera généralement du code qui dupliquera les objets manipulés par référence. On a donc une copie en deux étapes :

  • Le langage copie la source via memcpy, la copie possède des champs qui pointent vers les mêmes données que la source.

  • Le constructeur postblit duplique les données références.

 struct T
{
private:
int[] pi;
public:
/// Constructeur
this(size_t n)
{
pi = new int[n];
}

/// Constructeur postblit
this(this)
{
// duplique le tableau pointe par pi
pi = pi.dup;
}
}

Pourquoi un constructeur postblit ?

 

À ce stade-là, vous vous demanderez peut-être quel peut être l'intérêt du postblit constructeur par rapport à ce bon vieux constructeur de copie C++. C'est simple. Avec la contrainte que les objets doivent être déplaçables, une copie d'objets devient strictement équivalente à un déplacement + un appel à un hook défini par l'utilisateur (ici le postblit). Bon d'accord, le ramasse-miettes simplifie aussi un peu les choses.

Lorsque le compilateur détecte qu'il peut remplacer une copie par un déplacement, il est libre de ne pas insérer d'appel au constructeur postblit. En somme, D remplace la définition de 2 constructeurs (copie et déplacement) par la définition d'une seule méthode (postblit) qui ne sera pas appelée s'il n'y en a pas besoin.

Partager cet article

Repost 0
Published by Olivier - dans Programmation
commenter cet article

commentaires

Présentation

Recherche

Liens