C++
Diseño de tipos de objetos
Buscar..
Observaciones
Ver también Tamaño de tipos integrales .
Tipos de clase
Por "clase", nos referimos a un tipo que se definió usando la palabra clave class
o struct
(pero no enum struct
o enum class
).
Incluso una clase vacía aún ocupa al menos un byte de almacenamiento; Por lo tanto, consistirá puramente de relleno. Esto garantiza que si
p
apunta a un objeto de una clase vacía,p + 1
es una dirección distinta y apunta a un objeto distinto. Sin embargo, es posible que una clase vacía tenga un tamaño de 0 cuando se utiliza como clase base. Ver la optimización de la base vacía .class Empty_1 {}; // sizeof(Empty_1) == 1 class Empty_2 {}; // sizeof(Empty_2) == 1 class Derived : Empty_1 {}; // sizeof(Derived) == 1 class DoubleDerived : Empty_1, Empty_2 {}; // sizeof(DoubleDerived) == 1 class Holder { Empty_1 e; }; // sizeof(Holder) == 1 class DoubleHolder { Empty_1 e1; Empty_2 e2; }; // sizeof(DoubleHolder) == 2 class DerivedHolder : Empty_1 { Empty_1 e; }; // sizeof(DerivedHolder) == 2
La representación de objeto de un tipo de clase contiene las representaciones de objeto de la clase base y los tipos de miembros no estáticos. Por lo tanto, por ejemplo, en la siguiente clase:
struct S { int x; char* y; };
hay una secuencia consecutiva de bytes de
sizeof(int)
dentro de un objetoS
, llamado subobjeto, que contiene el valor dex
, y otro subobjeto con bytes desizeof(char*)
que contiene el valor dey
. Los dos no pueden ser intercalados.Si un tipo de clase tiene miembros y / o clases base con los tipos
t1, t2,...tN
, el tamaño debe ser al menossizeof(t1) + sizeof(t2) + ... + sizeof(tN)
dados los puntos anteriores . Sin embargo, dependiendo de los requisitos de alineación de los miembros y las clases base, el compilador puede verse obligado a insertar relleno entre subobjetos, o al principio o al final del objeto completo.struct AnInt { int i; }; // sizeof(AnInt) == sizeof(int) // Assuming a typical 32- or 64-bit system, sizeof(AnInt) == 4 (4). struct TwoInts { int i, j; }; // sizeof(TwoInts) >= 2 * sizeof(int) // Assuming a typical 32- or 64-bit system, sizeof(TwoInts) == 8 (4 + 4). struct IntAndChar { int i; char c; }; // sizeof(IntAndChar) >= sizeof(int) + sizeof(char) // Assuming a typical 32- or 64-bit system, sizeof(IntAndChar) == 8 (4 + 1 + padding). struct AnIntDerived : AnInt { long long l; }; // sizeof(AnIntDerived) >= sizeof(AnInt) + sizeof(long long) // Assuming a typical 32- or 64-bit system, sizeof(AnIntDerived) == 16 (4 + padding + 8).
Si el relleno se inserta en un objeto debido a los requisitos de alineación, el tamaño será mayor que la suma de los tamaños de los miembros y las clases base. Con la alineación de
n
bytes, el tamaño normalmente será el múltiplo más pequeño den
que es más grande que el tamaño de todos los miembros y clases base. Cada miembromemN
normalmente se coloca en una dirección que es un múltiplo dealignof(memN)
, yn
será típicamente la más grandealignof
de todos los miembrosalignof
s. Debido a esto, si un miembro con una menoralignof
es seguido por un miembro con una mayoralignof
, existe la posibilidad de que el último miembro no se alinee correctamente si se coloca inmediatamente después del anterior. En este caso, el relleno (también conocido como miembro de alineación ) se colocará entre los dos miembros, de modo que el último miembro pueda tener la alineación deseada. Por el contrario, si un miembro con unaalignof
másalignof
es seguido por un miembro con unaalignof
másalignof
, generalmente no será necesarioalignof
. Este proceso también se conoce como "embalaje".
Debido a que las clases normalmente comparten laalignof
de su miembro con laalignof
másalignof
, las clases típicamente se alinearán con laalignof
del tipo integrado más grande que contengan directa o indirectamente.// Assume sizeof(short) == 2, sizeof(int) == 4, and sizeof(long long) == 8. // Assume 4-byte alignment is specified to the compiler. struct Char { char c; }; // sizeof(Char) == 1 (sizeof(char)) struct Int { int i; }; // sizeof(Int) == 4 (sizeof(int)) struct CharInt { char c; int i; }; // sizeof(CharInt) == 8 (1 (char) + 3 (padding) + 4 (int)) struct ShortIntCharInt { short s; int i; char c; int j; }; // sizeof(ShortIntCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) + // 3 (padding) + 4 (int)) struct ShortIntCharCharInt { short s; int i; char c; char d; int j; }; // sizeof(ShortIntCharCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) + // 1 (char) + 2 (padding) + 4 (int)) struct ShortCharShortInt { short s; char c; short t; int i; }; // sizeof(ShortCharShortInt) == 12 (2 (short) + 1 (char) + 1 (padding) + 2 (short) + // 2 (padding) + 4 (int)) struct IntLLInt { int i; long long l; int j; }; // sizeof(IntLLInt) == 16 (4 (int) + 8 (long long) + 4 (int)) // If packing isn't explicitly specified, most compilers will pack this as // 8-byte alignment, such that: // sizeof(IntLLInt) == 24 (4 (int) + 4 (padding) + 8 (long long) + // 4 (int) + 4 (padding)) // Assume sizeof(bool) == 1, sizeof(ShortIntCharInt) == 16, and sizeof(IntLLInt) == 24. // Assume default alignment: alignof(ShortIntCharInt) == 4, alignof(IntLLInt) == 8. struct ShortChar3ArrShortInt { short s; char c3[3]; short t; int i; }; // ShortChar3ArrShortInt has 4-byte alignment: alignof(int) >= alignof(char) && // alignof(int) >= alignof(short) // sizeof(ShortChar3ArrShortInt) == 12 (2 (short) + 3 (char[3]) + 1 (padding) + // 2 (short) + 4 (int)) // Note that t is placed at alignment of 2, not 4. alignof(short) == 2. struct Large_1 { ShortIntCharInt sici; bool b; ShortIntCharInt tjdj; }; // Large_1 has 4-byte alignment. // alignof(ShortIntCharInt) == alignof(int) == 4 // alignof(b) == 1 // Therefore, alignof(Large_1) == 4. // sizeof(Large_1) == 36 (16 (ShortIntCharInt) + 1 (bool) + 3 (padding) + // 16 (ShortIntCharInt)) struct Large_2 { IntLLInt illi; float f; IntLLInt jmmj; }; // Large_2 has 8-byte alignment. // alignof(IntLLInt) == alignof(long long) == 8 // alignof(float) == 4 // Therefore, alignof(Large_2) == 8. // sizeof(Large_2) == 56 (24 (IntLLInt) + 4 (float) + 4 (padding) + 24 (IntLLInt))
Si se fuerza una alineación estricta con las alineaciones, se
alignas
relleno para forzar al tipo a cumplir con la alineación especificada, incluso cuando de lo contrario sería más pequeño. Por ejemplo, con la definición a continuación, los caracteresChars<5>
tendrán tres (o posiblemente más) bytes de relleno insertados al final, de modo que su tamaño total sea 8. No es posible que una clase con una alineación de 4 tenga un tamaño. de 5 porque sería imposible hacer una matriz de esa clase, por lo que el tamaño debe "redondearse" a un múltiplo de 4 insertando bytes de relleno.// This type shall always be aligned to a multiple of 4. Padding shall be inserted as // needed. // Chars<1>..Chars<4> are 4 bytes, Chars<5>..Chars<8> are 8 bytes, etc. template<size_t SZ> struct alignas(4) Chars { char arr[SZ]; }; static_assert(sizeof(Chars<1>) == sizeof(Chars<4>), "Alignment is strict.\n");
- Si dos miembros no estáticos de una clase tienen el mismo especificador de acceso , entonces se garantiza que el que viene más tarde en el orden de declaración vendrá más adelante en la representación del objeto. Pero si dos miembros no estáticos tienen diferentes especificadores de acceso, su orden relativo dentro del objeto no se especifica.
- No se especifica en qué orden aparecen los subobjetos de la clase base dentro de un objeto, ya sea que aparezcan consecutivamente, y si aparecen antes, después o entre subobjetos miembros.
Tipos aritméticos
Tipos de caracteres estrechos
El tipo unsigned char
utiliza todos los bits para representar un número binario. Por lo tanto, por ejemplo, si el unsigned char
tiene una longitud de 8 bits, los 256 patrones de bits posibles de un objeto char
representan los 256 valores diferentes {0, 1, ..., 255}. El número 42 está garantizado para ser representado por el patrón de bits 00101010
.
El tipo signed char
no tiene bits de relleno, es decir, si el signed char
tiene una longitud de 8 bits, tiene 8 bits de capacidad para representar un número.
Tenga en cuenta que estas garantías no se aplican a tipos que no sean tipos de caracteres estrechos.
Tipos enteros
Los tipos de enteros sin signo utilizan un sistema binario puro, pero pueden contener bits de relleno. Por ejemplo, es posible (aunque improbable) que un unsigned int
tenga una longitud de 64 bits pero solo sea capaz de almacenar enteros entre 0 y 2 32 - 1, ambos inclusive. Los otros 32 bits serían bits de relleno, que no deberían escribirse directamente.
Los tipos de enteros con signo utilizan un sistema binario con un bit de signo y posiblemente bits de relleno. Los valores que pertenecen al rango común de un tipo entero con signo y el tipo entero sin signo correspondiente tienen la misma representación. Por ejemplo, si el patrón de bits 0001010010101011
de un objeto unsigned short
representa el valor 5291
, entonces también representa el valor 5291
cuando se interpreta como un objeto short
.
Se define por la implementación si se utiliza el complemento de dos, el complemento de uno o la representación de magnitud de signo, ya que los tres sistemas satisfacen el requisito del párrafo anterior.
Tipos de punto flotante
La representación del valor de los tipos de punto flotante está definida por la implementación. Más comúnmente, los tipos float
y double
ajustan a IEEE 754 y tienen una longitud de 32 y 64 bits (así, por ejemplo, float
tendría 23 bits de precisión que seguirían 8 bits de exponente y 1 bit de signo). Sin embargo, la norma no garantiza nada. Los tipos de punto flotante a menudo tienen "representaciones de trampa", que causan errores cuando se usan en los cálculos.
Arrays
Un tipo de matriz no tiene relleno entre sus elementos. Por lo tanto, una matriz con el tipo de elemento T
es solo una secuencia de objetos T
dispuestos en memoria, en orden.
Una matriz multidimensional es una matriz de matrices, y lo anterior se aplica recursivamente. Por ejemplo, si tenemos la declaración.
int a[5][3];
entonces a
es una matriz de 5 matrices de 3 int
s. Por lo tanto, a[0]
, que consiste en los tres elementos a[0][0]
, a[0][1]
, a[0][2]
, se presenta en la memoria antes de a[1]
, que consiste en de a[1][0]
, a[1][1]
, y a[1][2]
. Esto se llama fila orden mayor .