2010-05-13 12 views
9

Dos citas del C++ estándar, §1.8:optimización clase base vacía

un objeto es una región de almacenamiento.

Subobjetos de clase base pueden tener un tamaño cero.

No creo que una región de almacenamiento pueda ser de tamaño cero. Eso significaría que algunos subobjetos de clase base no son realmente objetos. ¿Cómo coexisten estas declaraciones?

+1

¿Por qué crees que una región de almacenamiento no puede ser de tamaño cero? Aparentemente puede :) –

+1

Llamar malloc (0). – bmargulies

+1

@bmargulies 'malloc' está redactado específicamente en C para que no necesite crear un objeto de tamaño cero:" Si el tamaño del espacio solicitado es cero, el comportamiento está definido por la implementación: se devuelve un puntero nulo , o el comportamiento es como si el tamaño fuera un valor distinto de cero " –

Respuesta

10

No es necesario un argumento filosófico sobre la definición de "región".

1.8/5 dice: "A menos que sea un campo de bits, el objeto más derivado tendrá un tamaño distinto de cero ... Los subobjetos de la clase base pueden tener un tamaño cero".

Por lo tanto, el estándar es bastante claro qué objetos (y por lo tanto qué "regiones de almacenamiento") pueden tener tamaño cero. Si no está de acuerdo con el estándar, lo que significa "región" en inglés es una cosa, puede criticar las habilidades literarias de los autores (no relacionadas con la programación). Para el caso, puedes culpar a sus habilidades poéticas (14.7.3/7). Pero está bastante claro qué dice el estándar aquí sobre los tamaños de los objetos de los tipos de clase.

La forma pragmática de leer las normas es que, dadas dos interpretaciones plausibles de una palabra, elija una que no contradiga directamente otra oración en la misma sección de la norma. No elija el que coincida más estrechamente con su uso personal preferido de la palabra, ni siquiera con el uso más común.

+0

"A menos que sea un campo de bits, un objeto más derivado tendrá un tamaño distinto de cero" parece entrar en conflicto con 'new int [0]' que se dice que "asigna una matriz sin elementos". ¿Eso implica que las matrices realmente * do * contienen relleno interno en ese caso? –

+0

No creo que una matriz sea un objeto derivado en su mayoría. 1.8/4: "Si un objeto completo ... es del tipo de clase, su tipo se considera la clase más derivada ... un objeto del tipo de clase más derivado se llama el objeto más derivado" –

+0

@Steve dice " un objeto del tipo de clase más derivado o de un tipo no de clase se llama objeto más derivado ". - Editar: la redacción de C++ 03 parece diferir de la redacción de FCD. –

9

C++ no permite un objeto de tamaño cero, porque cada objeto debe tener una dirección de memoria única. Así que si usted tiene:

struct empty {}; 

// ... 

empty myempty; 
empty* ptr = &myempty; 

continuación ptr debe apuntar a una dirección de memoria única. El estándar establece que el tamaño mínimo para un objeto es de 1 byte para este propósito. De forma similar, las asignaciones de tamaño 0 están permitidas y devuelven un puntero válido, incluso si no se permite escribir en ese puntero (esto funciona para malloc(0), y new empty devuelve un puntero a un byte, desde sizeof(empty) == 1).

Si derivo de empty así:

struct derived : public empty 
{ 
    int data; 
} 

ya no hay ningún punto en la clase base empty que ocupa un byte, porque todo derived tendrá una dirección única, debido al miembro data. La cita "Subobjetos de clase base puede tener tamaño cero" está presente para permitir, en este caso, que el compilador no use ningún espacio para empty, tal que sizeof(derived) == 4. Como dice el título, es solo una optimización, y es perfectamente legal que empty parte de derived ocupe cero espacio.

+0

En realidad, 'sizeof (empty)' se permite que sea más grande, y la mayoría de los compiladores lo harán al menos el ancho de la palabra máquina. –

+0

"cada objeto debe tener una dirección de memoria única": a pesar de las palabras incorrectas en el estándar, no se requiere que cada objeto tenga una dirección de memoria única. Un objeto puede tener la misma dirección que un subobjeto al principio, y/o un subobjeto de clase base. Debería decir que cada objeto _complete_ debe tener una dirección única. – davmac

2

El C++ estándar 1.8.5 estados: -

menos que sea un campo de bits (9.6), un objeto derivado más tendrá un no-cero tamaño y deberá ocupar uno o más bytes de almacenamiento. Los objetos subobjetos de clase base pueden tener un tamaño cero. Un objeto de copia trivial o tipo de disposición estándar (3.9) deberá ocupar bytes de almacenamiento contiguos.

Por lo tanto, la norma alows una clase base que no tiene miembros de datos (y no los virtuales) para compartir la misma dirección que otra subobjeto con un tipo distinto. Se puede jugar con el tamaño de la clase base vacía como ...

struct a{}; 
struct a1{}; 
struct b : public a, public a1{char c;}; 


int main() 
{ 
    std::cout << sizeof(b) << "\n"; 
    std::cout << sizeof(b::a); 
} 


Which outputs (ignoring padding)... 

1 
1 


now try: 


struct a{}; 
struct b : public a {a ax;}; 


int main() 
{ 
    std::cout << sizeof(b) << "\n"; 
    std::cout << sizeof(b::a); 
} 


and the output is ... 

2 
1 

porque las dos instancias de una (como base y como un miembro) deben tener direcciones distintas.

BTW: "b :: a" es otra forma de decir "a". La presencia del operador de acceso al alcance no solicita el "subobjeto de clase base de b de tipo a". El versículo 5.3.3/2 dice: - Al aplicar sizeof a un subobjeto de clase base, el resultado es el tamaño del tipo de ese objeto.