История вопроса:
C++ строился на базе C, который предоставил небольшой набор встроенных типов: числа, символы, массивы. С развитием языка добавились такие понятия, как структуры, классы, перечисления — они стали пользовательскими типами.
Проблема:
Тип данных в программе определяет, сколько памяти займет переменная, как она будет инициализироваться, копироваться, уничтожаться, сравниваться. Встроенные типы имеют определенное стандартом поведение, пользовательские — требуют явного описания всех аспектов. Ошибки в управлении пользовательскими типами могут приводить к сбоям, утечкам, несправедливым сравнениям объектов и пр.
Решение:
Встроенные типы — это int, float, double, char, bool и прочие "примитивы". Пользовательские — любые структуры, классы, объединения, созданные вами. Для сложных задач нужно внедрять пользовательские типы с корректной семантикой копирования, сравнения, управления ресурсами.
Пример кода:
// Встроенный тип int x = 5; // Пользовательский тип struct Point { double x, y; }; Point a = {1.0, 2.0}; // Класс с ресурсами class MyFile { public: MyFile(const std::string& fn) : f(fopen(fn.c_str(), "r")) {} ~MyFile() { if (f) fclose(f); } private: FILE* f; };
Могут ли пользовательские типы вести себя полностью как встроенные (например, сравниваться через == "из коробки")?
Нет, сравнение == работает по умолчанию только начиная с C++20 через defaulted operator==, до этого требовалось явное определение.
Можно ли не реализовывать конструктор копирования и деструктор, если класс держит только сырой указатель (int, FILE, и т.д.)?**
Нет, в этом случае по умолчанию копирование будет "мелким", что приведет к утечкам или двойному освобождению памяти/ресурса. Нужно реализовывать "Rule of Five".
Будет ли значение структуры по умолчанию инициализировано нулями (Point p;)?
Нет, локальная структура может быть неинициализирована, содержимое памяти случайное. Использовать инициализацию явно.
Отрицательный кейс:
Класс-обертка над файлом не реализовал деструктор. Программа работала, пока не стала обрабатывать тысячи файлов без закрытия дескрипторов.
Плюсы: меньше кода, упрощенный вид Минусы: утечки ресурсов, нестабильное поведение
Положительный кейс:
Класс реализует RAII — файл открывается в конструкторе, закрывается в деструкторе, запрещено копирование.
Плюсы: надёжность, безопасность, чистый интерфейс Минусы: необходимость помнить о "правиле пяти" и написать больше кода