40 votes

Pourquoi sizeof (BaseClass) == sizeof (DerivedClass) alors que j’ajoute un membre

À partir du code ci-dessous sizeof(Base) == 24 et sizeof(Derived) == 24 .

Pourquoi leurs tailles sont-elles égales?

Dans la classe Base , nous avons 3 membres et dans la classe Derived , nous avons un autre membre.

 class Base
{
private:
    double d;
protected:
    long l;
public:
    int i;
};

class Derived : public Base
{
private:
    float f;
};
 

51voto

AndreyT Points 139512

Il se trouve que votre classe Base a 8 octets d'alignement, mais son dernier membre a la taille 4. Cela conduit à un vide remplissage de la zone d'ajouter à la fin de l' Bases'disposition de la mémoire. Que rembourrage supplémentaire joue son rôle d'instancier des objets de la classe Base par eux-mêmes, comme c'est appelé la plupart des objets dérivés.

Base b; // <- a most-derived object
Base a[10]; // <- an array of most-derived objects

Toutefois, lorsque vous "intégrer" Base comme classe de base dans la classe Derived, il n'y a pas besoin de rembourrage supplémentaire à la fin de l'embedded Base sous-objet.

Derived d; // <- object `d` contains an embedded sub-object of type `Base`

Une smart compilateur va tenter de réutiliser cette zone en plaçant le champ supplémentaire de classe Derived dans la zone d'affichage utilisé pour le remplissage en Base. Dans votre cas, le champ supplémentaire Derived::f a d'ailleurs la même taille de 4 octets, c'est à dire qu'il s'adapte-il à la perfection. Le résultat final est que la taille totale de la classe ne permet pas d'augmenter.

Une très similaires (dans la nature) effet est appelé "vide optimisation de la base". En C++ sizeof pour tout type est garanti pour être plus grand que 0, ce qui signifie qu' sizeof d'une classe vide est toujours plus grande que zéro. Toutefois, lorsque vous dérivez classe à partir d'un vide de la classe de base, vous pouvez observer que la classe de base contribue exactement 0 octets à la dérivée de la classe de taille. Par exemple

struct A {};
struct B {};
struct C {};
struct D {};

struct F : A, B, C, D {
  int i;
}

int main() {
  std::cout << sizeof(A) << std::endl << sizeof(B) << std::endl << 
               sizeof(C) << std::endl << sizeof(D) << std::endl;
  std::cout << sizeof(F) << std::endl;
}

Même si sizeof de chaque classe de base est supérieure à zéro, sizeof(F) généralement encore évaluer à l' sizeof(int), comme si la classe de base sous-objets n'existent pas.

En d'autres termes, comme ces exemples le montrent, la classe de base sous-objets suivez nettement plus détendue règles relatives à leur disposition de la mémoire que la plupart des objets dérivés. Ces règles assouplies pourrait facilement conduire à des situations lors de l' sizeof de la classe de base ne sera que partiellement de contribuer à la sizeof de la classe dérivée.

9voto

galop1n Points 4079

Parce que vous avez sizeof (double) == sizeof (long) == 8 et cela signifie souvent que alignof (double) est égal à 8 également. Cela signifie que la base doit avoir une taille alignée sur une limite de 8 octets dans le cas où elle est stockée dans un tableau et qu'elle génère un remplissage de 4 octets à la fin. Derived supprime la mise à f à la place.

5voto

peppe Points 4869

Utilisez pahole pour le comprendre:

 class Base {
private:

    double                     d;                    /*     0     8 */
protected:

    long int                   l;                    /*     8     8 */
    int                        i;                    /*    16     4 */


    /* size: 24, cachelines: 1, members: 3 */
    /* padding: 4 */
    /* last cacheline: 24 bytes */
};
class Derived : public Base {
public:

    /* class Base                <ancestor>; */      /*     0    24 */

    /* XXX last struct has 4 bytes of padding */
private:

    /* Bitfield combined with next fields */

    float                      f;                    /*    20     4 */


    /* size: 24, cachelines: 1, members: 2 */
    /* paddings: 1, sum paddings: 4 */
    /* last cacheline: 24 bytes */
};
 

2voto

Adam D. Ruppe Points 10493

Rembourrage en raison de l'harmonisation des besoins:

Bien que le compilateur (ou interprète) normalement alloués par personne les éléments de données sur le tracé des limites, des structures de données ont souvent des membres de la avec les différentes exigences alignement. Pour maintenir un bon alignement le traducteur insère normalement sans nom supplémentaire de données des membres que chaque membre est correctement aligné. En outre, la structure de données l'ensemble peut être rembourré avec une finale sans nom de membre. Cela permet à chaque membre d'un tableau de structures afin d'être correctement alignées.

Plus à ce sujet ici:
http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X