C ++: ¿Cuál es el tamaño de un objeto de una clase vacía?

111

Me preguntaba cuál podría ser el tamaño de un objeto de una clase vacía . Seguramente no podría tener 0 bytes, ya que debería ser posible hacer referencia y señalarlo como cualquier otro objeto. Pero, ¿qué tan grande es un objeto así?

Usé este pequeño programa:

#include <iostream>
using namespace std;

class Empty {};

int main()
{
    Empty e;
    cerr << sizeof(e) << endl;
    return 0;
}

¡El resultado que obtuve en los compiladores de Visual C ++ y Cygwin-g ++ fue de 1 byte ! Esto me sorprendió un poco, ya que esperaba que fuera del tamaño de la palabra de la máquina (32 bits o 4 bytes).

¿Alguien puede explicar por qué el tamaño de 1 byte? ¿Por qué no 4 bytes? ¿Esto depende del compilador o también de la máquina? Además, ¿alguien puede dar una razón más convincente de por qué un objeto de clase vacío no tendrá un tamaño de 0 bytes?

Ashwin Nanjappa
fuente
No veo ninguna razón por la que no pueda ser cero. Pero al darle un tamaño, otras cosas son más fáciles en el compilador. Si tiene una variedad de estas cosas, entonces cada elemento necesita una dirección única. Un tamaño de 1 lo hace fácil.
Martin York
2
Puede ser de tamaño cero si es un subobjeto de clase base.
Johannes Schaub - litb

Respuestas:

129

Citando las Preguntas frecuentes sobre estilo y técnica de C ++ de Bjarne Stroustrup , la razón por la que el tamaño no es cero es "Para garantizar que las direcciones de dos objetos diferentes sean diferentes". Y el tamaño puede ser 1 porque la alineación no importa aquí, ya que no hay nada que realmente mirar.

Sol
fuente
55
Bah, ¿qué diablos iba a saber sobre C ++? :-)
paxdiablo
18
@Lazer, porque no hay estructuras vacías en C.
aib
7
@nurabha Este puntero apunta al objeto. No está almacenado en el objeto. Sin embargo, si hay funciones virtuales, el objeto contiene un puntero a vtable.
tbleher
4
@krish_oza: piensas mal. Cuando asigna una variable en la pila, no puede ocupar cero bytes (porque diferentes variables necesitan diferentes direcciones), de ahí el tamaño mínimo de 1. Después de eso, depende del compilador. Similarmente con new; cuando se asigna el espacio, otra asignación debe tener una dirección diferente. Y así. No hay necesariamente ningún puntero involucrado en la clase vacía; puede haber punteros a las variables (o referencias, o asignaciones locales / de pila), pero no son de tamaño cero y no son necesariamente de tamaño de puntero.
Jonathan Leffler
3
@Destructor, no más inteligente, pero sé lo que es un emoticón :-) Es posible que desee volver a leer el comentario con esa luz y darse cuenta de que era humor.
paxdiablo
30

El estándar establece que todos los objetos derivados tienen sizeof ()> = 1:

A menos que sea un campo de bits (class.bit), el objeto más derivado tendrá un tamaño distinto de cero y ocupará uno o más bytes de almacenamiento. Los subobjetos de clase base pueden tener un tamaño cero. Objeto de introducción ISO / IEC FDIS 14882: 1998 (E)

TrayMan
fuente
1
Me parece difícil de creer. El estándar hace todo lo posible para asegurarse de que las implementaciones tengan las manos libres para hacer un buen trabajo de optimización atando las manos del implementador de esa manera no suena como el tipo de cosas que normalmente hace el estándar (podría estar equivocado)
Martin York
4
@eSKay: esto es necesario para que diferentes objetos obtengan diferentes direcciones. No podría tener un mapa de punteros a objetos, por ejemplo, si diferentes instancias tuvieran la misma dirección.
Brian Neal
14

Eso es realmente un detalle de implementación. Hace mucho tiempo, pensé que podría ser de cero bytes o mil bytes, que no tiene nada que ver con la especificación del lenguaje. Pero, después de mirar el estándar (sección 5.3.3), sizeofse define como devolver siempre uno o más, pase lo que pase.

El tamaño de la clase más derivada será mayor que cero.

Esto es necesario, entre otras cosas, para permitirle manejar matrices de objetos y apuntadores a ellos. Si a sus elementos se les permitiera tener un tamaño cero, &(array[0])serían idénticos a &(array[42]), lo que causará todo tipo de estragos en sus ciclos de procesamiento.

La razón por la que puede no ser una palabra de máquina es que no hay elementos dentro de ella que realmente requieran que esté alineada en un límite de palabra (como un número entero). Por ejemplo, si coloca char x; int y;dentro de la clase, mi GCC lo registra en ocho bytes (ya que el segundo int debe estar alineado en esa implementación).

paxdiablo
fuente
La razón del "no cero" es que diferentes objetos deben tener diferentes direcciones. Imagina una matriz de objetos de tamaño cero. ¿Cómo lo indexaría? Sin embargo, en algunos casos, el compilador puede optimizar esto (la optimización de la clase base vacía)
jalf
@jalf: "¿Cómo lo indexarías?" ¿De la misma manera que lo haría en C (para una matriz de objetos de estructura, por ejemplo)?
Lazer
1
@eSKay: no podrías si tuvieran tamaño 0. Todos estarían en el elemento 0.
Brian Neal
1
@BrianNeal que no es problema, ya que no tienen un estado para diferenciarse. Los problemas solo llegan cuando se consideran los punteros.
gha.st
2
O tomando el tamaño de dicha matriz para calcular su longitud.
gha.st
6

Hay una excepción: matrices de longitud 0

#include <iostream>

class CompletlyEmpty {
  char NO_DATA[0];
};

int main(int argc, const char** argv) {
  std::cout << sizeof(CompletlyEmpty) << '\n';
}
Konstantin Nikitin
fuente
¿Por qué nadie ha comentado esta respuesta? Me parece muy curioso.
Peregring-lk
Si crea dos objetos "CompletelyEmpty" ('a' y 'b' por ejemplo), sizeof dice que tienen 0 bytes de longitud, pero sus direcciones son diferentes ('& a == & b' se evalúa como falso). Y esto debería ser imposible ... (usando g ++ 4.7.2).
Peregring-lk
1
Pero si crea una matriz de estos objetos, digamos c, &c[M] == &c[N]para cada My N(clang ++ 4.1).
Konstantin Nikitin
5
C y C ++ no permiten matrices de longitud cero. Su compilador podría, pero es incorrecto.
Konrad Borowski
sí, el tamaño de la clase en sí es cero, pero una instancia de esta clase sigue siendo de 1 byte.
ZeR0
5

Aunque no es necesario asignar memoria para una clase vacía, pero para hacer objetos de clases vacías, el compilador asigna la memoria mínima que se puede asignar, que es 1 byte. De esta manera, el compilador puede distinguir dos objetos de la misma clase vacía de forma única y podrá asignar la dirección del objeto a un puntero del tipo de clase vacía.

lalatendu
fuente
3

Esto puede ayudarlo :-) http://bytes.com/topic/c/insights/660463-sizeof-empty-class-structure-1-a

El tamaño de una clase o estructura vacía es 1

La razón por la que esto sucede se reduce a la implementación adecuada del estándar, una de las cosas que dice el estándar C ++ es que "ningún objeto tendrá la misma dirección en la memoria que cualquier otra variable" ... ¿Cuál es la forma más fácil de asegurar esto? Asegúrese de que todos los tipos tengan un tamaño distinto de cero. Para lograr esto, el compilador agrega un byte ficticio a las estructuras y clases que no tienen miembros de datos ni funciones virtuales para que tengan un tamaño de 1 en lugar de un tamaño de 0 y luego se garantiza que tendrán una dirección de memoria única.

Arsalan Mehmood
fuente
2

La asignación de 1 byte para una clase vacía depende del compilador. Los compiladores deben asegurarse de que los objetos residan en diferentes ubicaciones de memoria y deben asignar un tamaño de memoria distinto de cero a un objeto. Escuche las notas sobre este tema aquí: http://listenvoice.com/listenVoiceNote.aspx?id=27

Aunque los compiladores asignan un tamaño distinto de cero a una clase vacía, también realizan optimizaciones cuando se derivan nuevas clases de clases vacías. Escuche sobre la optimización de la base vacía en las preguntas de la entrevista de programación en c ++ de ListenVoice.

usuario332764
fuente
2

la razón de la clase sin miembros de datos pero con un tamaño de 1 byte es que este * texto fuerte * debe almacenarse en la memoria para que una referencia o puntero pueda apuntar al objeto de esa clase

Ramiz
fuente
0

clase vacía: esa clase no tiene ningún contenido.

cualquier clase que no esté vacía estará representada por su contenido en la memoria.

ahora, ¿cómo se representará la clase vacía en la memoria? como no tiene contenido no hay forma de mostrar su existencia en la memoria, pero la clase está presente, es obligatorio mostrar su presencia en la memoria. Para mostrar la presencia de clase vacía en la memoria, se requiere 1 byte.

Ganesh
fuente
0

Creo que es así porque como 1 byte es la unidad de memoria más pequeña que se puede usar como marcador de posición, y no puede dar un tamaño cero, ya que no será posible crear una matriz de objetos.

y lo que dijiste "Esto me sorprendió un poco, ya que esperaba que fuera del tamaño de la palabra de la máquina (32 bits o 4 bytes)". será verdadero para la variable de referencia (palabras macine) de tipo vacío (), no el tamaño de la clase en sí (que es un tipo de datos abstracto),

Lázaro
fuente
0

Creo que esta pregunta es solo de interés teórico, pero no importa en la práctica.

Como ya han señalado otros, derivar de una clase vacía no hace ningún daño, ya que esto no consumirá memoria adicional para la parte de la clase base.

Además, si una clase está vacía (lo que significa que, en teoría, no necesita ninguna memoria por instancia, es decir, no tiene miembros de datos no estáticos o funciones de miembros virtuales), entonces todas sus funciones de miembros también pueden (y debería) definirse como estático. Por tanto, no es necesario crear una instancia de esta clase.

En pocas palabras: si se encuentra escribiendo una clase X vacía, haga que todas las funciones miembro sean estáticas. Entonces no necesitará crear objetos X, y las clases derivadas no se verán afectadas de ninguna manera.

kidfisto
fuente
0
#include<iostream>
using namespace std;


    class Empty { };
    int main()
    {
        Empty* e1 = new Empty;
        Empty* e2 = new Empty;

        if (e1 == e2)
            cout << "Alas same address of two objects" << endl;
        else
            cout << "Okay it's Fine to have different addresses" << endl;

        return 0;
    }

Salida: Está bien tener diferentes direcciones

Devolver el tamaño 1 asegura que los dos objetos no tendrán la misma dirección.

Sandeep_black
fuente
0

Es distinto de cero para garantizar que los dos objetos diferentes tengan direcciones diferentes. Los diferentes objetos deben tener diferentes direcciones, por lo que el tamaño de una clase vacía es siempre de 1 byte.

Yadvendra Yadav
fuente
-2

Creo que si el tamaño de una clase vacía es cero, significa que no existe. Para que exista (clase), requiere tener al menos 1 byte, ya que este byte es la dirección de memoria / referencia.

Miel
fuente
-3

Es debido a este puntero, aunque el puntero es (entero) de 4 bytes, pero se refiere a una ubicación de memoria (una Unidad) que es de 1 byte.

Narayan das khatri
fuente
5
Lo siento, pero esto es una tontería.
jogojapan
@Narayan das khatri: primero, el tipo de puntero depende del tipo de datos, no siempre es int y segundo, el tamaño del puntero depende de la máquina y el compilador en máquinas de 32 bits es de 4 bytes y para máquinas de 64 bits es de 8 bytes.
Krishna Oza