Namespaces
Variants

Objeto

De es.cppreference.net
 
 
Lenguaje C++
Temas generales
Control de flujo
Sentencias de ejecución condicional
if
Sentencias de iteración (bucles)
for
range-for (C++11)
Sentencias de salto
Funciones
Declaración de función
Expresión de función lambda
inline especificador
Especificaciones de excepción dinámicas (hasta C++17*)
noexcept especificador (C++11)
Excepciones
Espacios de nombres
Tipos
Especificadores
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Especificadores de duración de almacenamiento
Inicialización
Expresiones
Representaciones alternativas
Literales
Booleanos - Enteros - Punto flotante
Caracteres - Cadenas - nullptr (C++11)
Definidos por el usuario (C++11)
Utilidades
Atributos (C++11)
Tipos
typedef declaración
Declaración de alias de tipo (C++11)
Conversiones
Asignación de memoria
Clases
Propiedades de función específicas de clase
explicit (C++11)
static

Funciones miembro especiales
Plantillas
Misceláneos
 
 

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

Un objeto, en C++, tiene

Las siguientes entidades no son objetos: valor, referencia, función, enumerador, tipo, miembro de clase no estático, 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, introducida por una declaración.

Creación de objetos

Los objetos pueden ser creados explícitamente mediante definiciones, new expresiones, throw expresiones, cambiando el miembro activo de una unión y evaluando expresiones que requieren objetos temporales. El objeto creado se define de manera única en la creación explícita de objetos.

Los objetos de tipos de tiempo de vida implícito también pueden ser creados implícitamente mediante

  • excepto durante la evaluación constante, las operaciones que inician el tiempo de vida de un arreglo de tipo unsigned char o std::byte(desde C++17), en cuyo caso dichos objetos se crean en el arreglo,
  • llamada a las siguientes funciones de asignación, en cuyo caso dichos objetos se crean en el almacenamiento asignado:
  • operator new (excepto durante la evaluación constante)
  • operator new[] (excepto durante la evaluación constante)
  • std::malloc
  • std::calloc
  • std::realloc
  • std::aligned_alloc
(desde C++17)
  • llamada a las siguientes funciones de copia de representación de objeto, en cuyo caso dichos objetos se crean en la región de almacenamiento de destino o en el resultado:
  • std::memcpy
  • std::memmove
(desde C++20)
  • llamada a las siguientes funciones específicas, en cuyo caso dichos objetos se crean en la región de almacenamiento especificada:
  • std::start_lifetime_as
  • std::start_lifetime_as_array
(desde C++23)

Pueden crearse cero o más objetos en la misma región de almacenamiento, siempre que hacerlo dé al programa un comportamiento definido. Si dicha creación es imposible, p. ej. debido a operaciones conflictivas, el comportamiento del programa es indefinido. Si múltiples conjuntos de objetos creados implícitamente dieran al programa un comportamiento definido, no se especifica qué conjunto de objetos se crea. En otras palabras, los objetos creados implícitamente no necesitan estar definidos de manera única.

Después de crear implícitamente objetos dentro de una región de almacenamiento especificada, 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 si 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()
{
    // One of possible defined behaviors:
    // the call to std::malloc implicitly creates an object of type X
    // and its subobjects a and b, and returns a pointer to that X object
    X* p = static_cast<X*>(std::malloc(sizeof(X)));
    p->a = 1;
    p->b = 2;
    return p;
}

La llamada a std::allocator::allocate o a funciones miembro especiales de copia/movimiento definidas implícitamente de tipos unión también puede crear objetos.

Representación de objeto y representación de valor

Algunos tipos y objetos tienen representaciones de objeto y representaciones de valor, se definen en la tabla a continuación:

Entidad Representación de objeto Representación de valor
un tipo de objeto completo T la secuencia de N unsigned char objetos ocupados por un objeto completo no campo de bits de tipo T, donde N es sizeof(T) el conjunto de bits en la representación de objeto de T que participan en representar un valor de tipo T
un objeto completo no campo de bits obj de tipo T los bytes de obj correspondientes a la representación de objeto de T los bits de obj correspondientes a la representación de valor de T
un objeto campo de bits bf la secuencia de N bits ocupados por bf, donde N es el ancho del campo de bits el conjunto de bits en la representación de objeto de bf que participan en representar el valor de bf

Los bits en la representación de objeto de un tipo u objeto que no forman parte de la representación de valor son bits de relleno.

Para tipos TrivialmenteCopiable, la representación de valor es parte de la representación de 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 de trampa de su tipo y cargarlo en la CPU genera una excepción de hardware, como valores de punto flotante SNaN ("signalling not-a-number") o enteros NaT ("not-a-thing")).

Aunque la mayoría de las implementaciones no permiten representaciones de trampa, bits de relleno o representaciones múltiples para tipos enteros, hay excepciones; por ejemplo, un valor de un tipo entero en Itanium puede ser una representación de trampa.

Lo contrario no es necesariamente cierto: dos objetos de un tipo TrivialmenteCopiable 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 campos de bits, etc.

#include <cassert>

struct S
{
    char c;  // 1 byte value
             // 3 bytes of padding bits (assuming alignof(float) == 4)
    float f; // 4 bytes value (assuming sizeof(float) == 4)
    
    bool operator==(const S& arg) const // value-based equality
    {
        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'; // modify some padding bits
    assert(s1 == s2); // value did not change
}

Para los objetos de tipo char, signed char, unsigned char, char8_t(desde C++20) (a menos que sean campos de bits de tamaño excesivo), se requiere que cada bit de la representación de objeto participe en la representación de valor y cada posible patrón de bits represente un valor distinto (no se permiten bits de relleno, bits de trampa o representaciones múltiples).

Subobjetos

Un objeto puede tener subobjetos. Estos incluyen

  • objetos miembro
  • subobjetos de clase base
  • elementos de arreglo

Un objeto que no es subobjeto de otro objeto se llama objeto completo.

Si un objeto completo, un subobjeto miembro o un elemento de arreglo es de tipo clase, su tipo se considera la clase más derivada , para distinguirlo del tipo de clase de cualquier subobjeto de clase base. Un objeto de un tipo de clase más derivada o de un tipo no clase se llama objeto más derivado .

Para una clase,

se llaman 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 [[no_unique_address]] atributo(desde C++20).

Un objeto obj solo puede tener tamaño cero si se cumplen todas las condiciones siguientes:

  • obj es un subobjeto potencialmente superpuesto.
  • obj es de un tipo clase sin funciones miembro virtuales ni clases base virtuales.
  • obj no tiene ningún subobjeto de tamaño distinto de cero ni campos de bits sin nombre de longitud distinta de cero.

Para un objeto obj que cumple todas las condiciones anteriores:

  • Si obj es un subobjeto de clase base de un tipo de clase de diseño estándar(desde C++11) sin miembros de datos no estáticos, tiene tamaño cero.
  • De lo contrario, está definido por la implementación bajo qué circunstancias obj tiene tamaño cero.

Véase optimización de base vacía para más detalles.

Cualquier objeto que no sea campo de bits y tenga 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ón

A 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:

  • a es un subobjeto de b.
  • b proporciona almacenamiento para a.
  • Existe un objeto c donde a está anidado dentro de c, y c está anidado dentro de b.

Un objeto es un objeto potencialmente no único si es uno de los siguientes objetos:

(desde C++11)
(desde C++26)
  • Un subobjeto de un objeto potencialmente no único.

Para dos objetos que no sean campos de bits con tiempos de vida superpuestos:

  • Si se cumple alguna de las siguientes condiciones, pueden tener la misma dirección:
  • Uno de ellos está anidado dentro del otro.
  • Cualquiera de ellos es un subobjeto de tamaño cero, y sus tipos no son similares.
  • Ambos son objetos potencialmente no únicos.
  • De lo contrario, siempre tienen direcciones distintas y ocupan bytes de almacenamiento disjuntos.
// character literals are always unique
static const char test1 = 'x';
static const char test2 = 'x';
const bool b = &test1 != &test2;      // always true

// the character 'x' accessed from “r”, “s” and “il”
// may have the same address (i.e., these objects may share storage)
static const char (&r) [] = "x";
static const char *s = "x";
static std::initializer_list<char> il = {'x'};
const bool b2 = r != il.begin();      // unspecified result
const bool b3 = r != s;               // unspecified result
const bool b4 = il.begin() != &test1; // always true
const bool b5 = r != &test1;          // always true

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 toda implementación existente, es un puntero a menos que se optimice), que es utilizada por las llamadas a funciones virtuales y por las características RTTI (dynamic_cast y typeid) 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 se determina a partir de la expresión en la que se usa el objeto y se decide en tiempo de compilación.

#include <iostream>
#include <typeinfo>

struct Base1
{
    // polymorphic type: declares a virtual member
    virtual ~Base1() {}
};

struct Derived1 : Base1
{
     // polymorphic type: inherits a virtual member
};

struct Base2
{
     // non-polymorphic type
};

struct Derived2 : Base2
{
     // non-polymorphic type
};

int main()
{
    Derived1 obj1; // object1 created with type Derived1
    Derived2 obj2; // object2 created with type Derived2
    
    Base1& b1 = obj1; // b1 refers to the object obj1
    Base2& b2 = obj2; // b2 refers to the object 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';
}

Posible salida:

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

Aliasing 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, véase reinterpret_cast para la lista de excepciones y ejemplos.

Alineación

Cada 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.

El requisito de alineación de un tipo se puede consultar con alignof o std::alignment_of. La función de alineación de punteros std::align se puede usar para obtener un puntero adecuadamente alineado dentro de un buffer. std::aligned_storage se puede usar para obtener almacenamiento adecuadamente alineado.(hasta C++23)

(desde C++11)

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 alignas(desde C++11). Intentar crear un objeto en almacenamiento que no cumple los requisitos de alineación del tipo del objeto es comportamiento indefinido.

Para satisfacer los requisitos de alineación de todos los miembros no estáticos de una clase, se pueden insertar bits de relleno después de algunos de sus miembros.

#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 char, signed char y unsigned char, que es igual a 1; la alineación fundamental más grande de tipo escalar está definida por la implementación y es igual a la alineación de std::max_align_t(desde C++11).

Si una solicitud de una alineación extendida específica en un contexto específico no es soportada por una implementación, el programa está mal formado.

Si la alineación de un tipo se hace más estricta (mayor) que std::max_align_t, se conoce como un tipo con requisito de alineación extendida. Un tipo cuya alineación es extendida o un tipo clase cuyo miembro de datos no estático tiene alineación extendida es un tipo sobrealineado.

Se requiere que los tipos Allocator manejen correctamente los tipos sobrealineados.

(desde C++11)


Está definido por la implementación si new expresiones y(hasta C++17) std::get_temporary_buffer soportan tipos sobrealineados.

(desde C++11)
(hasta C++20)

Notas

Los objetos en C++ tienen un significado diferente al de los objetos en programación orientada a objetos (POO):

Objetos en C++ Objetos en POO
pueden tener cualquier tipo de objeto
(véase std::is_object)
deben tener un tipo clase
sin concepto de "instancia" tienen el concepto de "instancia" (y existen mecanismos como instanceof para detectar la relación "instancia-de")
sin concepto de "interfaz" tienen el concepto de "interfaz" (y existen mecanismos como instanceof para detectar si una interfaz está implementada)
el polimorfismo necesita estar explícitamente habilitado mediante miembros virtuales el polimorfismo siempre está habilitado

En el informe de defecto P0593R6, se consideraba que la creación implícita de objetos ocurría al crear un arreglo de bytes o al invocar una función de asignación (que posiblemente sea definida por el usuario y constexpr) durante la evaluación constante. Sin embargo, tal permiso causaba indeterminismo en la evaluación constante, lo cual era indeseado e inimplementable en algunos aspectos. Como resultado, P2747R2 deshabilitó dicha creación implícita de objetos en evaluación constante. Consideramos intencionalmente tal cambio como un informe de defecto aunque el documento completo no lo sea.

Informes de defectos

Los siguientes informes de defectos que cambian el comportamiento se aplicaron retroactivamente a estándares C++ publicados anteriormente.

DR Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 633 C++98 las variables solo podían ser objetos también pueden ser referencias
CWG 734 C++98 no se especificaba si las variables definidas
en el mismo ámbito que se garantiza que tengan
el mismo valor pueden tener la misma dirección
la dirección se garantiza que sea
diferente si sus tiempos de vida se solapan,
independientemente de sus valores
CWG 1189 C++98 dos subobjetos de clase base del mismo
tipo podían tener la misma dirección
siempre tienen
direcciones distintas
CWG 1861 C++98 para campos de bits de tamaño excesivo de tipos de carácter
estrecho, todos los bits de la representación de objeto
todavía participaban en la representación de valor
permite bits de relleno
CWG 2489 C++98 char[] no puede proporcionar almacenamiento, pero los objetos
podían crearse implícitamente dentro de su almacenamiento
los objetos no se pueden crear implícitamente
dentro del almacenamiento de char[]
CWG 2519 C++98 la definición de representación de objeto no abordaba los campos de bits aborda los campos de bits
CWG 2719 C++98 el comportamiento de crear un objeto
en almacenamiento desalineado no estaba claro
el comportamiento es
indefinido en este caso
CWG 2753 C++11 no estaba claro si un arreglo de respaldo de una
lista de inicializadores puede compartir almacenamiento con un literal de cadena
pueden compartir almacenamiento
CWG 2795 C++98 al determinar si dos objetos con tiempos de vida
superpuestos pueden tener la misma dirección, si alguno de ellos es un
subobjeto de tamaño cero, podrían tener tipos distintos similares
solo permite tipos no similares
P0593R6 C++98 el modelo de objeto anterior no soportaba muchos
modismos útiles requeridos por la biblioteca estándar
y no era compatible con tipos efectivos en C
se añadió la creación implícita de objetos

Véase también