empty_bases

Section spécifique à Microsoft

La norme C++ exige qu’un objet le plus dérivé ait une taille différente de zéro et qu’il occupe un ou plusieurs octets de stockage. Étant donné que l’exigence s’étend uniquement aux objets dérivés les plus dérivés, les sous-objets de classe de base ne sont pas soumis à cette contrainte. L’optimisation de la classe de base vide (EBCO) tire parti de cette liberté. Cela entraîne une réduction de la consommation de mémoire, ce qui peut améliorer les performances. Le compilateur Microsoft Visual C++ a toujours eu une prise en charge limitée d’EBCO. Dans Visual Studio 2015 Update 3 et versions ultérieures, nous avons ajouté un nouvel __declspec(empty_bases) attribut pour les types de classes qui tirent pleinement parti de cette optimisation.

Important

L’utilisation d’ABI __declspec(empty_bases) peut entraîner une modification cassant la structure et la disposition de classe où elle est appliquée. Assurez-vous que tout le code client utilise les mêmes définitions pour les structures et les classes que votre code lorsque vous utilisez cet attribut de classe de stockage.

Syntaxe

__declspec( empty_bases )

Notes

Dans Visual Studio, absent __declspec(align())alignas() ou spécification, une classe vide est de 1 octet de taille :

struct Empty1 {};
static_assert(sizeof(Empty1) == 1, "Empty1 should be 1 byte");

Une classe avec un seul membre de données non statique de type char est également de taille de 1 octet :

struct Struct1
{
  char c;
};
static_assert(sizeof(Struct1) == 1, "Struct1 should be 1 byte");

La combinaison de ces classes dans une hiérarchie de classes entraîne également une classe de taille de 1 octet :

struct Derived1 : Empty1
{
  char c;
};
static_assert(sizeof(Derived1) == 1, "Derived1 should be 1 byte");

Ce résultat est l’optimisation de la classe de base vide au travail, comme sans elle Derived1 serait de 2 octets de taille : 1 octet pour Empty1 et 1 octet pour Derived1::c. La disposition de classe est également optimale lorsqu’il existe une chaîne de classes vides :

struct Empty2 : Empty1 {};
struct Derived2 : Empty2
{
  char c;
};
static_assert(sizeof(Derived2) == 1, "Derived2 should be 1 byte");

Toutefois, la disposition de classe par défaut dans Visual Studio ne tire pas parti d’EBCO dans plusieurs scénarios d’héritage :

struct Empty3 {};
struct Derived3 : Empty2, Empty3
{
  char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // Error

Bien qu’elle Derived3 puisse être de 1 octet, la disposition de classe par défaut entraîne la taille de 2 octets. L’algorithme de disposition de classe ajoute 1 octet de remplissage entre deux classes de base vides consécutives, ce qui entraîne effectivement Empty2 la consommation d’un octet supplémentaire dans Derived3:

class Derived3  size(2):
   +---
0  | +--- (base class Empty2)
0  | | +--- (base class Empty1)
   | | +---
   | +---
1  | +--- (base class Empty3)
   | +---
1  | c
   +---

Les effets de cette disposition non optimale sont composés lorsque les exigences d’alignement d’une classe de base ou d’un sous-objet membre ultérieur forcent le remplissage supplémentaire :

struct Derived4 : Empty2, Empty3
{
  int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // Error

L’alignement naturel d’un objet de type int est de 4 octets, donc 3 octets de remplissage supplémentaire doivent être ajoutés après Empty3 pour aligner Derived4::icorrectement :

class Derived4 size(8):
   +---
0  | +--- (base class Empty2)
0  | | +--- (base class Empty1)
   | | +---
   | +---
1  | +--- (base class Empty3)
   | +---
   | <alignment member> (size=3)
4  | i
   +---

Un autre problème lié à la disposition de classe par défaut est qu’une classe de base vide peut être disposée à un décalage au-delà de la fin de la classe :

struct Struct2 : Struct1, Empty1
{
};
static_assert(sizeof(Struct2) == 1, "Struct2 should be 1 byte");
class Struct2 size(1):
   +---
0  | +--- (base class Struct1)
0  | | c
   | +---
1  | +--- (base class Empty1)
   | +---
   +---

Bien qu’il Struct2 s’agit de la taille optimale, Empty1 elle est disposée à décalage 1, Struct2 mais la taille de Struct2 celle-ci n’est pas augmentée pour en tenir compte. Par conséquent, pour un tableau A d’objets Struct2 , l’adresse du Empty1 sous-objet est A[0] identique à l’adresse de A[1], qui ne doit pas être le cas. Ce problème ne se produit pas s’il Empty1 a été disposé au décalage 0 au sein Struct2du sous-objet, ce qui chevauche le Struct1 sous-objet.

L’algorithme de disposition par défaut n’a pas été modifié pour répondre à ces limitations et tirer pleinement parti d’EBCO. Une telle modification interrompt la compatibilité binaire. Si la disposition par défaut d’une classe a changé à la suite d’EBCO, chaque fichier objet et bibliothèque qui contient la définition de classe doit être recompilé afin qu’ils acceptent tous la disposition de classe. Cette exigence s’étend également aux bibliothèques obtenues à partir de sources externes. Les développeurs de ces bibliothèques doivent fournir des versions indépendantes compilées avec et sans la disposition EBCO pour prendre en charge les clients qui utilisent différentes versions du compilateur. Bien que nous ne puissions pas modifier la disposition par défaut, nous pouvons fournir un moyen de modifier la disposition par classe avec l’ajout de l’attribut __declspec(empty_bases) de classe. Une classe définie avec cet attribut peut utiliser pleinement EBCO.

struct __declspec(empty_bases) Derived3 : Empty2, Empty3
{
  char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // No Error
class Derived3  size(1):
   +---
0  | +--- (base class Empty2)
0  | | +--- (base class Empty1)
   | | +---
   | +---
0  | +--- (base class Empty3)
   | +---
0  | c
   +---

Tous les sous-objets de Derived3 sont disposés à décalage 0, et sa taille est la valeur optimale de 1 octet. L’un des points importants à retenir est que __declspec(empty_bases) seule la disposition de la classe à laquelle elle est appliquée. Elle n’est pas appliquée de manière récursive aux classes de base :

struct __declspec(empty_bases) Derived5 : Derived4
{
};
static_assert(sizeof(Derived5) == 4, "Derived5 should be 4 bytes"); // Error
class Derived5  size(8):
   +---
0  | +--- (base class Derived4)
0  | | +--- (base class Empty2)
0  | | | +--- (base class Empty1)
   | | | +---
   | | +---
1  | | +--- (base class Empty3)
   | | +---
   | | <alignment member> (size=3)
4  | | i
   | +---
   +---

Bien qu’il __declspec(empty_bases) soit appliqué à Derived5, il n’est pas éligible pour EBCO, car il n’a pas de classes de base vides directes, de sorte qu’il n’a aucun effet. Toutefois, si elle est appliquée à la Derived4 classe de base, qui est éligible à EBCO, les deux Derived4 et Derived5 aura une disposition optimale :

struct __declspec(empty_bases) Derived4 : Empty2, Empty3
{
  int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // No Error

struct Derived5 : Derived4
{
};
static_assert(sizeof(Derived5) == 4, "Derived5 should be 4 bytes"); // No Error
class Derived5  size(4):
   +---
0  | +--- (base class Derived4)
0  | | +--- (base class Empty2)
0  | | | +--- (base class Empty1)
   | | | +---
   | | +---
0  | | +--- (base class Empty3)
   | | +---
0  | | i
   | +---
   +---

En raison de l’exigence que tous les fichiers et bibliothèques d’objets acceptent la disposition de classe, __declspec(empty_bases) ne peuvent être appliqués qu’aux classes que vous contrôlez. Elle ne peut pas être appliquée aux classes de la bibliothèque standard ou aux classes incluses dans les bibliothèques qui ne sont pas également recompilées avec la disposition EBCO.

FIN de la section spécifique à Microsoft

Voir aussi

__declspec
Mots clés