C++ Типы данных

При программировании на языке программирования требуются различные переменные для хранения различных видов информации. Переменная сохраняет ячейку памяти значения, которое она хранит. Это означает, что при создании переменной в памяти резервируется некоторое пространство.

Вам может потребоваться хранить информацию различных типов данных (таких как символ, широкий символ, целое число, число с плавающей запятой, число с плавающей запятой двойной точности, логическое значение и т. д.). Операционная система выделит память в зависимости от типа данных переменной и определит, что хранить в зарезервированной памяти.

Базовые встроенные типы

C++ предоставляет программистам богатое разнообразие встроенных типов данных и пользовательских типов данных. В следующей таблице перечислены семь базовых типов данных C++:

TypeКлючевые слова
Логический типbool
Символьный типchar
целое числоint
Тип с плавающей запятойfloat
Тип с плавающей запятой двойной точностиdouble
Void void
Тип широкого символа

wchar_t

Фактически wchar_t определяется так:

typedef short int wchar_t;

Следовательно, wchar_t фактически занимает столько же места, сколько short int.

Некоторые базовые типы можно модифицировать одним или несколькими модификаторами типа:

МодификаторыОписаниеПример
signedОбозначает знаковый тип (по умолчанию)signed int x = -10;
unsignedОбозначает беззнаковый типunsigned int y = 10;
shortОбозначает короткий целочисленный типshort int z = 100;
longОбозначает длинный целочисленный типlong int a = 100000;
constУказывает на константу, значение нельзя изменитьconst int b = 5;
volatileУказывает, что переменная может быть неожиданно изменена, что запрещает оптимизацию компилятора.volatile int c = 10;
mutableУказывает, что члены класса могут бытьconstизменены в объектеmutable int counter;

В следующей таблице показан объём памяти, необходимый для различных типов переменных при хранении значений в памяти, а также максимальные и минимальные значения, которые может хранить переменная этого типа.

Примечание:В разных системах могут быть различия; один байт — это 8 бит.

Примечание:По умолчанию int, short и long являются знаковыми.

Примечание:long int занимает 8 байт, а int всегда занимает 4 байта. Ранние компиляторы C определяли long int как занимающий 4 байта, а int — как занимающий 2 байта. Новые стандарты C/C++ совместимы с этой ранней настройкой.

Типы данныхОписаниеРазмер (байты)Пример диапазона/значения
boolЛогический тип, представляющий true или false1trueилиfalse
charСимвольный тип, обычно используется для хранения символов ASCII.1от -128 до 127 или от 0 до 255
signed charЗнаковый символьный тип1от -128 до 127
unsigned charБеззнаковый символьный тип1от 0 до 255
wchar_tШирокий символьный тип, используемый для хранения символов Unicode.2 или 4Зависит от платформы
char16_t16-битный символьный тип Unicode (введён в C++11)2от 0 до 65,535
char32_t32-битный символьный тип Unicode (введён в C++11)4от 0 до 4,294,967,295
shortкороткое целое число2от -32,768 до 32,767
unsigned shortБеззнаковое короткое целое2от 0 до 65,535
intцелое число4-2,147,483,648 до 2,147,483,647
unsigned intБеззнаковый целочисленный тип4от 0 до 4,294,967,295
longдлинное целое4 или 8Зависит от платформы
unsigned longБеззнаковое длинное целое4 или 8Зависит от платформы
long longЦелое типа long long (введено в C++11)8-9,223,372,036,854,775,808 до 9,223,372,036,854,775,807
unsigned long longБеззнаковое целое типа long long (введено в C++11)80 до 18,446,744,073,709,551,615
floatЧисло с плавающей запятой одинарной точности4Приблизительно ±3.4e±38 (6-7 значащих цифр)
doubleЧисла с плавающей запятой двойной точности8Примерно ±1.7e±308 (15 значащих цифр)
long doubleЧисла с плавающей запятой расширенной точности8, 12 или 16Зависит от платформы

Новые типы, добавленные в C++11

Типы данныхОписаниеПример
autoАвтоматическое выведение типаauto x = 10;
decltypeПолучение типа выраженияdecltype(x) y = 20;
nullptrКонстанта нулевого указателяint* ptr = nullptr;
std::initializer_listТип списка инициализацииstd::initializer_list<int> list = {1, 2, 3};
std::tupleТип кортежа, который может хранить несколько значений разных типов.std::tuple<int, float, char> t(1, 2.0, 'a');

Обратите внимание, что размер хранения различных типов связан с разрядностью системы, но в настоящее время основными являются в основном 64-битные системы.

Ниже перечислены различия в размере хранения между 32-битными и 64-битными системами (в Windows то же самое):

Из таблицы выше видно, что размер переменных будет варьироваться в зависимости от компилятора и используемого компьютера.

Следующий пример выведет размер различных типов данных на вашем компьютере.

Пример

#include<iostream> #include <limits> using namespace std; int main() { cout << "тип: \t\t" << "************размер**************"<< endl; cout << "bool: \t\t" << "Количество занимаемых байтов:" << sizeof(bool); cout << "\Максимальное значение t:" << (numeric_limits<bool>::max)(); cout << "\t\Минимальное значение t:" << (numeric_limits<bool>::min)() << endl; cout << "char: \t\t" << "Количество занимаемых байтов:" << sizeof(char); cout << "\Максимальное значение t:" << (numeric_limits<char>::max)(); cout << "\t\Минимальное значение t:" << (numeric_limits<char>::min)() << endl; cout << "signed char: \t" << "Количество занимаемых байтов:" << sizeof(signed char); cout << "\Максимальное значение t:" << (numeric_limits<signed char>::max)(); cout << "\t\Минимальное значение t:" << (numeric_limits<signed char>::min)() << endl; cout << "unsigned char: \t" << "Количество занимаемых байтов:" << sizeof(unsigned char); cout << "\Максимальное значение t:" << (numeric_limits<unsigned char>::max)(); cout << "\t\Минимальное значение t:" << (numeric_limits<unsigned char>::min)() << endl; cout << "wchar_t: \t" << "Количество занимаемых байтов:" << sizeof(wchar_t); cout << "\Максимальное значение t:" << (numeric_limits<wchar_t>::max)(); cout << "\t\Минимальное значение t:" << (numeric_limits<wchar_t>::min)() << endl; cout << "short: \t\t" << "Количество занимаемых байтов:" << sizeof(short); cout << "\Максимальное значение t:" << (numeric_limits<short>::max)(); cout << "\t\Минимальное значение t:" << (numeric_limits<short>::min)() << endl; cout << "int: \t\t" << "Количество занимаемых байтов:" << sizeof(int); cout << "\Максимальное значение t:" << (numeric_limits<int>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<int>::min)() << endl; cout << "unsigned: \t" << "Количество занимаемых байтов:" << sizeof(unsigned); cout << "\Максимальное значение t:" << (numeric_limits<unsigned>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<unsigned>::min)() << endl; cout << "long: \t\t" << "Количество занимаемых байтов:" << sizeof(long); cout << "\Максимальное значение t:" << (numeric_limits<long>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<long>::min)() << endl; cout << "unsigned long: \t" << "Количество занимаемых байтов:" << sizeof(unsigned long); cout << "\Максимальное значение t:" << (numeric_limits<unsigned long>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<unsigned long>::min)() << endl; cout << "double: \t" << "Количество занимаемых байтов:" << sizeof(double); cout << "\Максимальное значение t:" << (numeric_limits<double>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<double>::min)() << endl; cout << "long double: \t" << "Количество занимаемых байтов:" << sizeof(long double); cout << "\Максимальное значение t:" << (numeric_limits<long double>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<long double>::min)() << endl; cout << "float: \t\t" << "Количество занимаемых байтов:" << sizeof(float); cout << "\Максимальное значение t:" << (numeric_limits<float>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<float>::min)() << endl; cout << "size_t: \t" << "Количество занимаемых байтов:" << sizeof(size_t); cout << "\Максимальное значение t:" << (numeric_limits<size_t>::max)(); cout << "\Минимальное значение t:" << (numeric_limits<size_t>::min)() << endl; cout << "string: \t" << "Количество занимаемых байтов:" << sizeof(string) << endl; // << "\tMaximum value: " << (numeric_limits<string>::max)() << "\tMinimum value: " << (numeric_limits<string>::min)() << endl; cout << "тип: \t\t" << "************размер**************"<< endl; return 0; }

В этом примере используетсяendl, это вставит символ новой строки после каждой строки,<<Операторы используются для передачи нескольких значений на экран,sizeof()Оператор используется для получения размера различных типов данных.

При компиляции и выполнении приведённого выше кода будут получены следующие результаты, которые могут отличаться в зависимости от используемого компьютера:

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**************

Производные типы данных

Типы данныхОписаниеПример
ArrayКоллекция элементов одного типаint arr[5] = {1, 2, 3, 4, 5};
PointerТип, который хранит адрес памяти переменнойint* ptr = &x;
ReferenceПсевдоним переменнойint& ref = x;
FunctionТип функции, представляющий сигнатуру функцииint func(int a, int b);
StructПользовательский тип данных, который может содержать несколько членов разных типов.struct Point { int x; int y; };
ClassПользовательский тип данных, поддерживающий инкапсуляцию, наследование и полиморфизм.class MyClass { ... };
UnionНесколько членов совместно используют один и тот же блок памятиunion Data { int i; float f; };
EnumПользовательский набор целочисленных константenum Color { RED, GREEN, BLUE };

Псевдоним типа

ПсевдонимОписаниеПример
typedefОпределить псевдоним для существующего типаtypedef int MyInt;
usingОпределить псевдоним для существующего типа (введено в C++11)using MyInt = int;

Тип стандартной библиотеки

Типы данныхОписаниеПример
std::stringСтроковый типstd::string s = "Hello";
std::vectorДинамический массивstd::vector<int> v = {1, 2, 3};
std::arrayМассив фиксированного размера (представлен в C++11)std::array<int, 3> a = {1, 2, 3};
std::pairКонтейнер, хранящий два значенияstd::pair<int, float> p(1, 2.0);
std::mapКонтейнер для пар «ключ-значение»std::map<int, std::string> m;
std::setНабор уникальных значенийstd::set<int> s = {1, 2, 3};

Объявление typedef

Можно использоватьtypedef, чтобы дать существующему типу новое имя. Ниже приведён синтаксис определения нового типа с помощью typedef:

typedef type newname; 

Например, следующая инструкция сообщает компилятору, что feet — это другое имя для int:

typedef int feet;

Теперь следующее объявление полностью допустимо; оно создаёт целочисленную переменную distance:

feet distance;

Перечисляемый тип

Перечисляемый тип (перечисление) — это производный тип данных в C++, представляющий собой набор нескольких определяемых пользователем констант перечисления.

Если переменная может принимать лишь несколько возможных значений, её можно определить как перечисляемый тип. Так называемое «перечисление» означает перечисление значений переменной по одному, и значение переменной может находиться только в пределах перечисленных значений.

Чтобы создать enum, нужно использовать ключевое словоenum. Общий вид перечисляемого типа:

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

Если перечисление не инициализировано, т. е. когда "= целочисленная константа" опущено, оно начинается с первого идентификатора.

Например, следующий код определяет перечисление color, а тип переменной c — color. В итоге c присваивается значение "blue".

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

По умолчанию значение первого имени равно 0, значение второго имени равно 1, значение третьего имени равно 2 и так далее. Однако можно также присвоить имени специальное значение, просто добавив начальное значение. Например, в следующем перечислении,greenЗначение равно 5.

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

Здесь,blueимеет значение 6, потому что по умолчанию каждое имя на 1 больше имени перед ним, но значение red всё ещё равно 0.


Преобразование типов

Преобразование типов — это преобразование значения одного типа данных в значение другого типа данных.

В C++ существует четыре вида преобразования типов: статическое преобразование, динамическое преобразование, преобразование const и reinterpret-преобразование.

Статическое приведение

Статическое преобразование — это принудительное преобразование значения одного типа данных в значение другого типа данных.

Статическое преобразование обычно используется для преобразований между объектами похожих типов, например для преобразования типа int в тип float.

Статическое приведение не выполняет никакой проверки типов во время выполнения, поэтому оно может привести к ошибкам времени выполнения.

Пример

int i = 10; float f = static_cast<float>(i); // Статически преобразовать тип int в тип float

Динамическое приведение

Динамическое преобразование (dynamic_cast) — это механизм в C++, используемый для понижающего приведения в иерархии наследования.

Динамическое преобразование обычно используется для преобразования указателя или ссылки на базовый класс в указатель или ссылку на производный класс.

Динамическое приведение выполняет проверку типов во время выполнения. Если преобразование не удаётся, оно возвращает nullptr для типов указателей и выбрасывает исключение std::bad_cast для типов ссылок.

Синтаксис:

dynamic_cast<目标类型>(表达式)
  • Целевой тип: Должен быть типом указателя или ссылки.

  • Выражение: Указатель или ссылка на базовый класс, которые необходимо преобразовать.

Пример: динамическое приведение типов указателей

#include <iostream> class Base { public: virtual ~Base() = default; // Базовый класс должен иметь виртуальные функции }; class Derived : public Base { public: void show() { std::cout << "метод производного класса" << std::endl; } }; int main() { Base* ptr_base = new Derived; // Указатель базового класса указывает на объект производного класса // Преобразование указателя базового класса в указатель производного класса Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); if (ptr_derived) { ptr_derived->show(); // Преобразование выполнено успешно, вызвать метод производного класса } else { std::cout << "Динамическое приведение не удалось!" << std::endl; } delete ptr_base; return 0; }

Вывод:

Derived class method

Пример: динамическое приведение ссылочных типов

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() = default; // Базовый класс должен иметь виртуальные функции
};

class Derived : public Base {
public:
    void show() {
        std::cout << "Метод производного класса" << std::endl;
    }
};

int main() {
    Derived derived_obj;
    Base& ref_base = derived_obj; // Ссылка базового класса, привязанная к объекту производного класса

    try {
        // Преобразовать ссылку базового класса в ссылку производного класса
        Derived& ref_derived = dynamic_cast<Derived&>(ref_base);
        ref_derived.show(); // Преобразование успешно, вызвать метод производного класса
    } catch (const std::bad_cast& e) {
        std::cout << Сбой динамического приведения: " << e.what() << std::endl;
    }

    return 0;
}

Вывод:

Derived class method
ОсобенностиТип указателяТип ссылки
Возвращаемое значение при неудачном преобразованииreturnnullptrГенерация исключенияstd::bad_castИсключение
Применимые сценарииПонижающее приведение, проверка типов во время выполненияПонижающее приведение, проверка типов во время выполнения
Накладные расходы на производительностьПовышенныйПовышенный
Требование к базовому классуДолжен иметь виртуальные функцииДолжен иметь виртуальные функции

Приведение const

Преобразование const используется для преобразования объекта типа const в объект неконстантного типа.

Преобразование const можно использовать только для отбрасывания атрибута const; оно не может изменить тип объекта.

Пример

const int i = 10; int& r = const_cast<int&>(i); // const_cast, преобразование const int в int

Переинтерпретирующее приведение

Переинтерпретирующее преобразование интерпретирует значение одного типа данных как значение другого типа данных, обычно используется для преобразований между разными типами данных.

Переинтерпретирующее преобразование не выполняет никакой проверки типов, что может привести к неопределённому поведению.

Пример

int i = 10; float f = reinterpret_cast<float&>(i); // reinterpret_cast из типа int в тип float
Другие расширения