C++ Tipos de datos

Al programar con un lenguaje de programación, se necesitan varias variables para almacenar diversa información. La variable conserva la ubicación de memoria del valor que almacena. Esto significa que, cuando se crea una variable, se reserva algo de espacio en la memoria.

Es posible que necesite almacenar información de varios tipos de datos (como tipo carácter, carácter ancho, entero, flotante, doble flotante, booleano, etc.). El sistema operativo asignará memoria y decidirá qué almacenar en la memoria reservada según el tipo de dato de la variable.

Tipos integrados básicos

C++ proporciona a los programadores una amplia variedad de tipos de datos integrados y tipos de datos definidos por el usuario. La siguiente tabla enumera los siete tipos de datos básicos de C++:

TipoPalabra clave
Booleanobool
Carácterchar
Enteroint
Flotantefloat
Doble flotantedouble
Sin tipo void
Carácter ancho

wchar_t

En realidad, wchar_t se obtiene así:

typedef short int wchar_t;

Por lo tanto, el espacio real de wchar_t es el mismo que el de short int.

Algunos tipos básicos se pueden modificar con uno o más modificadores de tipo:

ModificadorDescripciónEjemplo
signedRepresenta un tipo con signo (por defecto)signed int x = -10;
unsignedRepresenta un tipo sin signounsigned int y = 10;
shortRepresenta entero cortoshort int z = 100;
longRepresenta entero largolong int a = 100000;
constRepresenta una constante, el valor no se puede modificarconst int b = 5;
volatileRepresenta que la variable puede modificarse inesperadamente, prohíbe la optimización del compiladorvolatile int c = 10;
mutableRepresenta que los miembros de la clase pueden modificarse enconstel objetomutable int counter;

La siguiente tabla muestra la memoria que ocupan varios tipos de variables al almacenar valores, así como los valores máximo y mínimo que una variable de ese tipo puede almacenar.

Nota:Puede haber diferencias entre sistemas; un byte tiene 8 bits.

Nota:De forma predeterminada, int, short y long son con signo, es decir, signed.

Nota:long int ocupa 8 bytes, int ocupa 4 bytes; los primeros compiladores de C definieron long int con 4 bytes e int con 2 bytes. El nuevo estándar de C/C++ es compatible con esta configuración inicial.

Tipo de datosDescripciónTamaño (bytes)Rango/ejemplo de valores
boolTipo booleano, representa verdadero o falso1trueofalse
charTipo carácter, normalmente se usa para almacenar caracteres ASCII1-128 a 127 o 0 a 255
signed charTipo carácter con signo1-128 a 127
unsigned charTipo carácter sin signo10 a 255
wchar_tTipo carácter ancho, se usa para almacenar caracteres Unicode2 o 4Depende de la plataforma
char16_tTipo de carácter Unicode de 16 bits (introducido en C++11)20 a 65,535
char32_tTipo de carácter Unicode de 32 bits (introducido en C++11)40 a 4,294,967,295
shortEntero corto2-32,768 a 32,767
unsigned shortEntero corto sin signo20 a 65,535
intEntero4-2,147,483,648 a 2,147,483,647
unsigned intEntero sin signo40 a 4,294,967,295
longEntero largo4 o 8Depende de la plataforma
unsigned longEntero largo sin signo4 o 8Depende de la plataforma
long longEntero largo largo (introducido en C++11)8-9,223,372,036,854,775,808 a 9,223,372,036,854,775,807
unsigned long longEntero largo largo sin signo (introducido en C++11)80 a 18,446,744,073,709,551,615
floatNúmero de coma flotante de precisión simple4Aprox. ±3.4e±38 (6-7 dígitos significativos)
doubleNúmero de coma flotante de precisión doble8Aprox. ±1.7e±308 (15 dígitos significativos)
long doubleNúmero de coma flotante de precisión extendida8, 12 o 16Depende de la plataforma

Tipos nuevos en C++11

Tipo de datosDescripciónEjemplo
autoInferencia automática de tipoauto x = 10;
decltypeObtener el tipo de una expresióndecltype(x) y = 20;
nullptrConstante de puntero nuloint* ptr = nullptr;
std::initializer_listTipo de lista de inicializaciónstd::initializer_list<int> list = {1, 2, 3};
std::tupleTipo tupla, que puede almacenar varios valores de diferentes tiposstd::tuple<int, float, char> t(1, 2.0, 'a');

Tenga en cuenta que el tamaño de almacenamiento de cada tipo está relacionado con la cantidad de bits del sistema, aunque actualmente los sistemas de 64 bits son los más comunes.

A continuación se enumeran las diferencias de tamaño de almacenamiento entre los sistemas de 32 bits y los de 64 bits (igual en Windows):

De la tabla anterior se puede saber que el tamaño de las variables varía según el compilador y la computadora utilizada.

El siguiente ejemplo mostrará el tamaño de varios tipos de datos en su computadora.

Ejemplo

#include<iostream> #include <limits> using namespace std; int main() { cout << "type: \t\t" << "************size**************"<< endl; cout << "bool: \t\t" << "Bytes ocupados:" << sizeof(bool); cout << "\Valor máximo de t:" << (numeric_limits<bool>::max)(); cout << "\t\Valor mínimo de t:" << (numeric_limits<bool>::min)() << endl; cout << "char: \t\t" << "Bytes ocupados:" << sizeof(char); cout << "\Valor máximo de t:" << (numeric_limits<char>::max)(); cout << "\t\Valor mínimo de t:" << (numeric_limits<char>::min)() << endl; cout << "signed char: \t" << "Bytes ocupados:" << sizeof(signed char); cout << "\Valor máximo de t:" << (numeric_limits<signed char>::max)(); cout << "\t\Valor mínimo de t:" << (numeric_limits<signed char>::min)() << endl; cout << "unsigned char: \t" << "Bytes ocupados:" << sizeof(unsigned char); cout << "\Valor máximo de t:" << (numeric_limits<unsigned char>::max)(); cout << "\t\Valor mínimo de t:" << (numeric_limits<unsigned char>::min)() << endl; cout << "wchar_t: \t" << "Bytes ocupados:" << sizeof(wchar_t); cout << "\Valor máximo de t:" << (numeric_limits<wchar_t>::max)(); cout << "\t\Valor mínimo de t:" << (numeric_limits<wchar_t>::min)() << endl; cout << "short: \t\t" << "Bytes ocupados:" << sizeof(short); cout << "\Valor máximo de t:" << (numeric_limits<short>::max)(); cout << "\t\Valor mínimo de t:" << (numeric_limits<short>::min)() << endl; cout << "int: \t\t" << "Bytes ocupados:" << sizeof(int); cout << "\Valor máximo de t:" << (numeric_limits<int>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<int>::min)() << endl; cout << "unsigned: \t" << "Bytes ocupados:" << sizeof(unsigned); cout << "\Valor máximo de t:" << (numeric_limits<unsigned>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<unsigned>::min)() << endl; cout << "long: \t\t" << "Bytes ocupados:" << sizeof(long); cout << "\Valor máximo de t:" << (numeric_limits<long>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<long>::min)() << endl; cout << "unsigned long: \t" << "Bytes ocupados:" << sizeof(unsigned long); cout << "\Valor máximo de t:" << (numeric_limits<unsigned long>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<unsigned long>::min)() << endl; cout << "double: \t" << "Bytes ocupados:" << sizeof(double); cout << "\Valor máximo de t:" << (numeric_limits<double>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<double>::min)() << endl; cout << "long double: \t" << "Bytes ocupados:" << sizeof(long double); cout << "\Valor máximo de t:" << (numeric_limits<long double>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<long double>::min)() << endl; cout << "float: \t\t" << "Bytes ocupados:" << sizeof(float); cout << "\Valor máximo de t:" << (numeric_limits<float>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<float>::min)() << endl; cout << "size_t: \t" << "Bytes ocupados:" << sizeof(size_t); cout << "\Valor máximo de t:" << (numeric_limits<size_t>::max)(); cout << "\Valor mínimo de t:" << (numeric_limits<size_t>::min)() << endl; cout << "string: \t" << "Bytes ocupados:" << sizeof(string) << endl; // << "\t最大值:" << (numeric_limits<string>::max)() << "\t最小值:" << (numeric_limits<string>::min)() << endl; cout << "type: \t\t" << "************size**************"<< endl; return 0; }

Este ejemplo utilizaendlque insertará un salto de línea después de cada línea,<<El operador << se usa para enviar varios valores a la pantalla,sizeof()El operador sizeof se usa para obtener el tamaño de varios tipos de datos.

Cuando el código anterior se compila y ejecuta, produce los siguientes resultados; los resultados variarán según la computadora utilizada:

type:         ************size**************
bool:         所占字节数:1    最大值:1        最小值:0
char:         所占字节数:1    最大值:        最小值:?
signed char:     所占字节数:1    最大值:        最小值:?
unsigned char:     所占字节数:1    最大值:?        最小值:
wchar_t:     所占字节数:4    最大值:2147483647        最小值:-2147483648
short:         所占字节数:2    最大值:32767        最小值:-32768
int:         所占字节数:4    最大值:2147483647    最小值:-2147483648
unsigned:     所占字节数:4    最大值:4294967295    最小值:0
long:         所占字节数:8    最大值:9223372036854775807    最小值:-9223372036854775808
unsigned long:     所占字节数:8    最大值:18446744073709551615    最小值:0
double:     所占字节数:8    最大值:1.79769e+308    最小值:2.22507e-308
long double:     所占字节数:16    最大值:1.18973e+4932    最小值:3.3621e-4932
float:         所占字节数:4    最大值:3.40282e+38    最小值:1.17549e-38
size_t:     所占字节数:8    最大值:18446744073709551615    最小值:0
string:     所占字节数:24
type:         ************size**************

Tipos de datos derivados

Tipo de datosDescripciónEjemplo
arregloColección de elementos del mismo tipoint arr[5] = {1, 2, 3, 4, 5};
punteroTipo que almacena la dirección de memoria de una variableint* ptr = &x;
referenciaAlias de una variableint& ref = x;
funciónTipo de función, representa la firma de una funciónint func(int a, int b);
estructuraTipo de datos definido por el usuario, puede contener varios miembros de diferentes tiposstruct Point { int x; int y; };
claseTipo de datos definido por el usuario, admite encapsulación, herencia y polimorfismoclass MyClass { ... };
uniónVarios miembros comparten el mismo bloque de memoriaunion Data { int i; float f; };
enumeraciónConjunto de constantes enteras definidas por el usuarioenum Color { RED, GREEN, BLUE };

Alias de tipo

AliasDescripciónEjemplo
typedefDefinir un alias para un tipo existentetypedef int MyInt;
usingDefinir un alias para un tipo existente (introducido en C++11)using MyInt = int;

Tipos de la biblioteca estándar

Tipo de datosDescripciónEjemplo
std::stringTipo de cadenastd::string s = "Hello";
std::vectorArreglo dinámicostd::vector<int> v = {1, 2, 3};
std::arrayArreglo de tamaño fijo (introducido en C++11)std::array<int, 3> a = {1, 2, 3};
std::pairContenedor para almacenar dos valoresstd::pair<int, float> p(1, 2.0);
std::mapContenedor de pares clave-valorstd::map<int, std::string> m;
std::setConjunto de valores únicosstd::set<int> s = {1, 2, 3};

Declaración typedef

Puede utilizartypedefpara dar un nuevo nombre a un tipo existente. A continuación se muestra la sintaxis para definir un nuevo tipo con typedef:

typedef type newname; 

Por ejemplo, la siguiente declaración le indica al compilador que feet es otro nombre para int:

typedef int feet;

Ahora, la siguiente declaración es completamente legal y crea una variable entera distance:

feet distance;

Tipo enumerado

El tipo enumerado (enumeration) es un tipo de datos derivado en C++, que es un conjunto de varias constantes de enumeración definidas por el usuario.

Si una variable solo tiene algunos valores posibles, se puede definir como tipo enumerado (enumeration). El llamado "enumerar" significa listar los valores de la variable uno a uno; el valor de la variable solo puede estar dentro del rango de los valores listados.

Para crear una enumeración, se necesita la palabra claveenum. La forma general del tipo enumerado es:

enum 枚举名{ 
     标识符[=整型常数], 
     标识符[=整型常数], 
... 
    标识符[=整型常数]
} 枚举变量;
    

Si la enumeración no está inicializada, es decir, se omite "= constante entera", entonces comienza desde el primer identificador.

Por ejemplo, el siguiente código define una enumeración de colores, y la variable c es de tipo color. Finalmente, a c se le asigna "blue".

enum color { red, green, blue } c;
c = blue;

De forma predeterminada, el valor del primer nombre es 0, el del segundo nombre es 1, el del tercero es 2, y así sucesivamente. Sin embargo, también puede asignar un valor especial a un nombre, solo necesita agregar un valor inicial. Por ejemplo, en la siguiente enumeración,greentiene el valor 5.

enum color { red, green=5, blue };

Aquí,bluetiene el valor 6, porque de forma predeterminada, cada nombre es 1 mayor que el nombre anterior, pero el valor de red sigue siendo 0.


Conversión de tipos

La conversión de tipos es convertir el valor de un tipo de datos al valor de otro tipo de datos.

En C++ hay cuatro conversiones de tipos: conversión estática, conversión dinámica, conversión de constante y conversión de reinterpretación.

Conversión estática (Static Cast)

La conversión estática es forzar el valor de un tipo de datos al valor de otro tipo de datos.

La conversión estática se usa generalmente para conversiones entre objetos de tipos similares, por ejemplo, convertir el tipo int al tipo float.

La conversión estática no realiza ninguna verificación de tipo en tiempo de ejecución, por lo que puede provocar errores en tiempo de ejecución.

Ejemplo

int i = 10; float f = static_cast<float>(i); // Convertir estáticamente el tipo int al tipo float

Conversión dinámica (Dynamic Cast)

La conversión dinámica (dynamic_cast) es un mecanismo en C++ utilizado para realizar downcasting dentro de una jerarquía de herencia.

La conversión dinámica se usa generalmente para convertir un puntero o referencia de clase base a un puntero o referencia de clase derivada.

La conversión dinámica realiza la verificación de tipos en tiempo de ejecución. Si la conversión falla, para tipos de puntero devuelve nullptr, y para tipos de referencia lanza una excepción std::bad_cast.

Sintaxis:

dynamic_cast<目标类型>(表达式)
  • Tipo de destino: debe ser un tipo de puntero o referencia.

  • Expresión: el puntero o referencia de clase base que se necesita convertir.

Ejemplo: conversión dinámica de tipos puntero

#include <iostream> class Base { public: virtual ~Base() = default; // La clase base debe tener funciones virtuales }; class Derived : public Base { public: void show() { std::cout << "Derived class method" << std::endl; } }; int main() { Base* ptr_base = new Derived; // El puntero de clase base apunta a un objeto de clase derivada // Convertir el puntero de clase base a puntero de clase derivada Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); if (ptr_derived) { ptr_derived->show(); // Conversión exitosa, llamar al método de la clase derivada } else { std::cout << "Dynamic cast failed!" << std::endl; } delete ptr_base; return 0; }

Salida:

Derived class method

Ejemplo: conversión dinámica de tipos de referencia

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() = default; // La clase base debe tener funciones virtuales
};

class Derived : public Base {
public:
    void show() {
        std::cout << "Derived class method" << std::endl;
    }
};

int main() {
    Derived derived_obj;
    Base& ref_base = derived_obj; // La referencia de clase base se enlaza a un objeto de clase derivada

    try {
        // Convertir la referencia de clase base a referencia de clase derivada
        Derived& ref_derived = dynamic_cast<Derived&>(ref_base);
        ref_derived.show(); // Conversión exitosa, llamar al método de la clase derivada
    } catch (const std::bad_cast& e) {
        std::cout << "Dynamic cast failed: " << e.what() << std::endl;
    }

    return 0;
}

Salida:

Derived class method
CaracterísticaTipo punteroTipo referencia
Valor de retorno en caso de fallo de conversiónDevuelvenullptrLanzastd::bad_castexcepción
Escenario de aplicaciónDowncasting, verificación de tipos en tiempo de ejecuciónDowncasting, verificación de tipos en tiempo de ejecución
Sobrecarga de rendimientoAltaAlta
Requisito de la clase baseDebe tener funciones virtualesDebe tener funciones virtuales

Conversión de constante (Const Cast)

La conversión de constante se utiliza para convertir un objeto de tipo const a un objeto de tipo no const.

La conversión de constante solo se puede usar para eliminar el atributo const; no puede cambiar el tipo del objeto.

Ejemplo

const int i = 10; int& r = const_cast<int&>(i); // Conversión de constante, convertir const int a int

Conversión de reinterpretación (Reinterpret Cast)

La conversión de reinterpretación reinterpreta el valor de un tipo de datos como el valor de otro tipo de datos, y se usa generalmente para convertir entre diferentes tipos de datos.

La conversión de reinterpretación no realiza ninguna verificación de tipos, por lo que puede provocar un comportamiento indefinido.

Ejemplo

int i = 10; float f = reinterpret_cast<float&>(i); // Reinterpretar convirtiendo el tipo int al tipo float
Otras extensiones