Object
Los programas de C++ crean, destruyen, refieren, acceden y manipulan objetos .
Un objeto, en C++, tiene
-
tamaño (se puede determinar con
sizeof); -
requisito de alineación (se puede determinar con
alignof); - duración de almacenamiento (automática, estática, dinámica, local-de-hilo);
- tiempo de vida (limitado por la duración de almacenamiento o temporal);
- tipo ;
- valor (que puede ser indeterminado, p.ej. para tipos no-clase inicializados por defecto );
- opcionalmente, un nombre .
Las siguientes entidades no son objetos: valor, referencia, función, enumerador, tipo, miembro no estático de clase, plantilla, especialización de plantilla de clase o función, espacio de nombres, paquete de parámetros, y this .
Una variable es un objeto o una referencia que no es un miembro de datos no estático, que se introduce mediante una declaración .
Contenido
Creación de objetosLos objetos pueden ser creados explícitamente mediante definiciones , new expresiones , throw expresiones , cambiando el miembro activo de una union y evaluando expresiones que requieren objetos temporales . El objeto creado está únicamente definido en la creación explícita de objetos. Los objetos de tipos de duración implícita también pueden ser creados implícitamente mediante
Cero o más objetos pueden ser creados en la misma región de almacenamiento, siempre que hacerlo le dé al programa un comportamiento definido. Si dicha creación es imposible, por ejemplo, debido a operaciones conflictivas, el comportamiento del programa es indefinido. Si múltiples conjuntos de objetos creados implícitamente le dieran al programa un comportamiento definido, no está especificado cuál de dichos conjuntos de objetos es creado. En otras palabras, no se requiere que los objetos creados implícitamente estén definidos de manera única. Después de crear objetos implícitamente dentro de una región específica de almacenamiento, algunas operaciones producen un puntero a un objeto creado adecuado . El objeto creado adecuado tiene la misma dirección que la región de almacenamiento. Asimismo, el comportamiento es indefinido solo si ningún valor de puntero puede dar al programa un comportamiento definido, y no se especifica qué valor de puntero se produce si hay múltiples valores que dan al programa un comportamiento definido. #include <cstdlib> struct X { int a, b; }; X* MakeX() { // Uno de los comportamientos definidos posibles: // la llamada a std::malloc crea implícitamente un objeto de tipo X // y sus subobjetos a y b, y devuelve un puntero a ese objeto X X* p = static_cast<X*>(std::malloc(sizeof(X))); p->a = 1; p->b = 2; return p; } Llamada a std::allocator::allocate o las funciones miembro especiales de copia/movimiento definidas implícitamente de tipos union también pueden crear objetos. Representación de objeto y representación de valorAlgunos tipos y objetos tienen representaciones de objeto y representaciones de valor , se definen en la tabla siguiente:
Los bits en la representación del objeto de un tipo u objeto que no forman parte de la representación del valor son bits de relleno . Para los tipos TriviallyCopyable , la representación del valor es parte de la representación del objeto, lo que significa que copiar los bytes ocupados por el objeto en el almacenamiento es suficiente para producir otro objeto con el mismo valor (excepto si el objeto es un subobjeto potencialmente superpuesto, o el valor es una representación trampa de su tipo y cargarlo en la CPU genera una excepción de hardware, como los valores de punto flotante SNaN ("signalling not-a-number") o los enteros NaT ("not-a-thing")). Aunque la mayoría de las implementaciones no permiten representaciones trampa, bits de relleno o múltiples representaciones para tipos enteros, hay excepciones; por ejemplo un valor de un tipo entero en Itanium puede ser una representación trampa . Lo contrario no es necesariamente cierto: dos objetos de un tipo TriviallyCopyable con diferentes representaciones de objeto pueden representar el mismo valor. Por ejemplo, múltiples patrones de bits de punto flotante representan el mismo valor especial NaN . Más comúnmente, se pueden introducir bits de relleno para satisfacer requisitos de alineación , tamaños de campo de bits , etc. #include <cassert> struct S { char c; // valor de 1 byte // 3 bytes de bits de relleno (asumiendo alignof(float) == 4) float f; // valor de 4 bytes (asumiendo sizeof(float) == 4) bool operator==(const S& arg) const // igualdad basada en valor { return c == arg.c && f == arg.f; } }; void f() { assert(sizeof(S) == 8); S s1 = {'a', 3.14}; S s2 = s1; reinterpret_cast<unsigned char*>(&s1)[2] = 'b'; // modificar algunos bits de relleno assert(s1 == s2); // el valor no cambió } Para los objetos de tipo char , signed char , y unsigned char (a menos que sean campos de bits de tamaño excesivo), cada bit de la representación del objeto debe participar en la representación del valor y cada posible patrón de bits representa un valor distinto (no se permiten bits de relleno, bits trampa ni representaciones múltiples). SubobjetosUn objeto puede tener subobjetos . Estos incluyen
Un objeto que no es un subobjeto de otro objeto se denomina objeto completo . Si un objeto completo, un subobjeto miembro o un elemento de arreglo es de class type , su tipo se considera la clase más derivada , para distinguirlo del tipo de clase de cualquier subobjeto de clase base. Un objeto de tipo de clase más derivada o de tipo no-clase se denomina objeto más derivado . Para una clase,
se denominan sus subobjetos potencialmente construidos . Tamaño
Un subobjeto es un
subobjeto potencialmente superpuesto
si es un subobjeto de clase base
o un miembro de datos no estático declarado con el atributo
Un objeto obj solo puede posiblemente tener tamaño cero si se cumplen todas las siguientes condiciones:
Para un objeto obj que satisface todas las condiciones anteriores:
Consulte empty base optimization para más detalles. Cualquier objeto que no sea un campo de bits con tamaño distinto de cero debe ocupar uno o más bytes de almacenamiento, incluyendo cada byte que esté ocupado (total o parcialmente) por cualquiera de sus subobjetos. El almacenamiento ocupado debe ser contiguo si el objeto es de tipo trivialmente copiable o de diseño estándar (desde C++11) . DirecciónA menos que un objeto sea un campo de bits o un subobjeto de tamaño cero, la dirección de ese objeto es la dirección del primer byte que ocupa. Un objeto puede contener otros objetos, en cuyo caso los objetos contenidos están anidados dentro del objeto anterior. Un objeto a está anidado dentro de otro objeto b si se cumple alguna de las siguientes condiciones:
Un objeto es un objeto potencialmente no único si es uno de los siguientes objetos:
Para cualesquiera dos objetos que no sean campos de bits con tiempos de vida superpuestos:
// los literales de carácter siempre son únicos static const char test1 = 'x'; static const char test2 = 'x'; const bool b = &test1 != &test2; // siempre verdadero // el carácter 'x' accedido desde “r”, “s” e “il” // puede tener la misma dirección (es decir, estos objetos pueden compartir almacenamiento) static const char (&r) [] = "x"; static const char *s = "x"; static std::initializer_list<char> il = {'x'}; const bool b2 = r != il.begin(); // resultado no especificado const bool b3 = r != s; // resultado no especificado const bool b4 = il.begin() != &test1; // siempre verdadero const bool b5 = r != &test1; // siempre verdadero Objetos polimórficos
Los objetos de un tipo de clase que declara o hereda al menos una función virtual son objetos polimórficos. Dentro de cada objeto polimórfico, la implementación almacena información adicional (en todas las implementaciones existentes, es un puntero a menos que sea optimizado), que es utilizada por las llamadas a
funciones virtuales
y por las características de RTTI (
Para objetos no polimórficos, la interpretación del valor se determina a partir de la expresión en la que se utiliza el objeto, y se decide en tiempo de compilación.
Ejecutar este código
#include <iostream> #include <typeinfo> struct Base1 { // tipo polimórfico: declara un miembro virtual virtual ~Base1() {} }; struct Derived1 : Base1 { // tipo polimórfico: hereda un miembro virtual }; struct Base2 { // tipo no polimórfico }; struct Derived2 : Base2 { // tipo no polimórfico }; int main() { Derived1 obj1; // objeto1 creado con tipo Derived1 Derived2 obj2; // objeto2 creado con tipo Derived2 Base1& b1 = obj1; // b1 hace referencia al objeto obj1 Base2& b2 = obj2; // b2 hace referencia al objeto obj2 std::cout << "Expression type of b1: " << typeid(decltype(b1)).name() << '\n' << "Expression type of b2: " << typeid(decltype(b2)).name() << '\n' << "Object type of b1: " << typeid(b1).name() << '\n' << "Object type of b2: " << typeid(b2).name() << '\n' << "Size of b1: " << sizeof b1 << '\n' << "Size of b2: " << sizeof b2 << '\n'; } Salida posible: Expression type of b1: Base1 Expression type of b2: Base2 Object type of b1: Derived1 Object type of b2: Base2 Size of b1: 8 Size of b2: 1 Alias estricto
Acceder a un objeto usando una expresión de un tipo diferente al tipo con el que fue creado es comportamiento indefinido en muchos casos, consulte
AlineaciónCada tipo de objeto tiene la propiedad llamada requisito de alineación, que es un valor entero no negativo (de tipo std::size_t, y siempre una potencia de dos) que representa el número de bytes entre direcciones sucesivas en las que se pueden asignar objetos de este tipo.
Cada tipo de objeto impone su requisito de alineación en cada objeto de ese tipo; se puede solicitar una alineación más estricta (con un requisito de alineación mayor) usando Para satisfacer los requisitos de alineación de todos los miembros no estáticos de una clase, bits de relleno se pueden insertar después de algunos de sus miembros. Ejecutar este código #include <iostream>
// objects of type S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
char a; // size: 1, alignment: 1
char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
// objects of type X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
int n; // size: 4, alignment: 4
char c; // size: 1, alignment: 1
// three bytes of padding bits
}; // size: 8, alignment: 4
int main()
{
std::cout << "alignof(S) = " << alignof(S) << '\n'
<< "sizeof(S) = " << sizeof(S) << '\n'
<< "alignof(X) = " << alignof(X) << '\n'
<< "sizeof(X) = " << sizeof(X) << '\n';
}
Posible salida: alignof(S) = 1
sizeof(S) = 2
alignof(X) = 4
sizeof(X) = 8
La alineación más débil (el requisito de alineación más pequeño) es la alineación de Si una solicitud de una alineación extendida específica en un contexto específico no es compatible con una implementación, el programa está mal formado.
NotasLos objetos en C++ tienen un significado diferente al de los objetos en programación orientada a objetos (POO) :
En el informe de defectos
P0593R6
, se consideró que la creación implícita de objetos ocurría al crear un array de bytes o invocar una
función de asignación
(que posiblemente está definida por el usuario y es
Informes de defectosLos siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
Véase también
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||