Espacios de nombres
Variantes
Acciones

Objetos

De cppreference.com
< cpp‎ | language

Los programas de C++ crean, destruyen, referencian, acceden y manipulan objetos.

En C++, un objeto es una región de almacenamiento que (hasta C++14) tiene:

Las siguientes entidades no son objetos: valor, referencia, función, enumerado, tipo, miembro de clase no estático, campo de bits, plantilla, plantilla especializada de clase o función, espacio de nombres, paquete de parámetros y this.

Una variable es un objeto o referencia que no es un dato miembro no estático, que es introducido mediante una declaración.

Los objetos son creados mediante definiciones, expresiones new, expresiones throw, al cambiar el miembro activo de una unión, y donde se requieran objetos temporales.

Contenido

[editar] Representación de objeto y representación de valor

Para un objeto de tipo T, la representación de objeto es la secuencia de sizeof(T) objetos de tipo unsigned char (o el equivalente: std::byte) comenzando en la misma dirección que el objeto T.

La representación de valor de un objeto es el conjunto de bits que mantienen el valor de su tipo T

Para los tipos trivialmente copiables, la representación del valor es una 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 valor es una representación errónea de su tipo y al cargarlo en la CPU genera una excepción de hardware, como valores SNaN (señalización de no número) de punto flotante o enteros NaT (no una cosa)).

Lo contrario no es necesariamente verdadero: dos objetos de tipo trivialmente copiable con distintas representaciones de objeto pueden representar el mismo valor. Por ejemplo, varios patrones de bits de punto flotante representan el mismo valor especial NaN. Más comúnmente, algunos bits de la representación del objeto podrían no participar en la representación en lo absoluto; tales bits pueden ser introducidos como relleno para satisfacer los requisitos de alineación, los tamaños de los campos de bits, etc.

#include <cassert>
struct S {
    char c;  // valor de 1 byte 
             // 3 bytes de relleno
    float f; // valor de 4 bytes (suponiendo sizeof(float) == 4)
    bool operator==(const S& arg) const { // igualdad basada en el valor
        return c == arg.c && f == arg.f;
    }
};
assert(sizeof(S) == 8);
S s1 = {'a', 3.14};
S s2 = s1;
reinterpret_cast<char*>(&s1)[2] = 'b'; // modifica el segundo byte 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 gran tamaño), se requiere que cada bit de la representación del objeto participe en la representación del valor, y cada posible patrón de bits representa un valor distinto (no se permite relleno, trap bits o representación múltiple).

[editar] Subobjetos

Un objeto puede contener otros objetos, que se denominan subobjetos. Esto incluye:

  • objetos miembro;
  • subobjetos de la clase base;
  • elementos de array

Un objeto que no es subobjeto de otro se denomina un objeto completo.

Un subobjeto es potencialmente superpuesto si es bien:

  • un subobjeto de clase base, o
  • un dato miembro no estático declarado con el atributo [[no_unique_address]]

A los objetos completos, objetos miembro y elementos de array también se les conoce como objetos más derivados, para distinguirlos de los subobjetos de clase base. El tamaño de un objeto más derivado que no es un campo de bits y no está marcado [[no_unique_address]] (desde C++20) es necesariamente distinto de cero (el tamaño de un subobjeto de clase base puede ser cero incluso sin [[no_unique_address]] (desde C++20). Véase Optimización de base vacía).

Se garantiza que dos objetos con duraciones superpuestas (que no son campos de bits) tienen direcciones diferentes a menos que uno de ellos sea un subobjeto del otro o proporcione almacenamiento para el otro, o si son subobjetos de diferente tipo dentro del mismo objeto completo, y uno de ellos es subobjeto de tamaño cero.

static const char c1 = 'x';
static const char c2 = 'x';
assert(&c1 != &c2); // mismo valor, diferentes direcciones

[editar] Objetos polimórficos

Los objetos de un tipo 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 cada implementación existente, es un puntero a menos que esté optimizado), que se utiliza para llamadas a función virtual y por las características de RTTI (cast dinámico y id de tipo) para determinar, en tiempo de ejecución, el tipo con el que se creó el objeto, independientemente de la expresión en la que se use.

Para objetos no polimórficos, la interpretación del valor es determinada según la expresión en que se use, y se determina en tiempo de compilación.

#include <iostream>
#include <typeinfo>
struct Base1 {
    // tipo polimorfo: declara un miembro virtual
    virtual ~Base1() {}
};
struct Derivada1 : Base1 {
     // tipo polimorfo: hereda un miembro virtual
};
 
struct Base2 {
     // tipo no polimorfo
};
struct Derivada2 : Base2 {
     // tipo no polimórfico
};
 
int main()
{
    Derivada1 obj1; // obj1 se crea del tipo  Derivada1
    Derivada2 obj2; // obj2 se crea el tipo Derivada2
 
    Base1& b1 = obj1; // b1 referencia al objeto obj1
    Base2& b2 = obj2; // b2 referencia al objeto obj2
 
    std::cout << "Tipo de expresión de b1: " << typeid(decltype(b1)).name() << ' '
              << "Tipo de expresión de b2: " << typeid(decltype(b2)).name() << '\n'
              << "Tipo de objeto de b1: " << typeid(b1).name() << ' '
              << "Tipo de objeto de b2: " << typeid(b2).name() << '\n'
              << "tamaño de b1: " << sizeof b1 << ' '
              << "tamaño de b2: " << sizeof b2 << '\n';
}

Posible salida:

Tipo de expresión de b1: Base1 Tipo de expresión de b2: Base2
Tipo de objeto de b1: Derivada1 Tipo de objeto b2: Base2
tamaño de b1: 8 tamaño de b2: 1

[editar] Alias estricto

El acceso a un objeto usando una expresión de otro tipo distinto al tipo con el que fue creado, en muchos casos tiene un comportamiento indefinido. Véase reinterpret_cast para la lista de excepciones y ejemplos.

[editar] Alineamiento

Cada tipo objeto tiene la propiedad llamada requisitos de alineamiento, que es un valor entero (de tipo std::size_t, siempre potencia de 2) que representa el número de bytes entre direcciones sucesivas donde se pueden alojar objetos de este tipo. El requisito de alineamiento de un tipo se puede obtener mediante alignof o std::alignment_of. La función de alineamiento de puntero std::align se puede utilizar para obtener un puntero adecuadamente alineado dentro de cualquier búfer, y std::aligned_storage se puede usar para obtener un almacenamiento alineado adecuadamente.

Cada tipo objeto impone su requisito de alineamiento en cada objeto de ese tipo; se puede indicar una alineación más estricta (con mayor requisito de alineación) usando alignas.

Para satisfacer los requisitos de alineamiento de todos los miembros no estáticos de una clase, se puede insertar relleno después de algunos de sus miembros.

#include <iostream>
 
// los objetos de tipo S se pueden alojar en cualquier dirección
// porque S.a y S.b pueden ser alojados en cualquier dirección
struct S {
  char a; // tamaño: 1, alineación: 1
  char b; // tamaño: 1, alineación: 1
}; // tamaño: 2, alineación: 1
 
// los objetos de tipo X se deben alojar en bloques de 4 bytes
// porque X.n se debe alojar en bloques de 4 bytes
// porque el requisito de alineamiento de int es (normalmente) 4
struct X {
  int n;  // tamaño: 4, alineación: 4
  char c; // tamaño: 1, alineación: 1
  // tres bytes de relleno
}; // tamaño: 8, alineación: 4 
 
int main()
{
    std::cout << "sizeof(S) = " << sizeof(S)
              << " alignof(S) = " << alignof(S) << '\n';
    std::cout << "sizeof(X) = " << sizeof(X)
              << " alignof(X) = " << alignof(X) << '\n';
}

Posible salida:

sizeof(S) = 2 alignof(S) = 1
sizeof(X) = 8 alignof(X) = 4


La alineación más tenue (el requisito de alineamiento menor) es la alineación de char, signed char, y unsigned char, que es igual a 1; la alineación fundamental más grande de cualquier tipo es la alineación de std::max_align_t. Si la alineación de un tipo se hace más estricta (más grande) que std::max_align_t usando alignas, se conoce como un tipo con requisito de alineamiento extendido. Un tipo cuya alineación es extendida o un tipo clase cuyo miembro de datos no estático tiene una alineación extendida es un "tipo sobrealineado". Está definido en la implementación si la expresión new, std::allocator::allocate, y std::get_temporary_buffer admiten tipos sobrealineados. A los Asignadores instanciados con tipos sobrealineados se les permite fallar en la instancia en tiempo de compilación, lanzar std::bad_alloc en tiempo de ejecución, ignorar los requisitos de alineamiento no soportados, o manejarlos correctamente.

[editar] Véase también