Специальные методы классов
#новичкам
Помимо обычных методов, которые позволяют классу выполнить какую-то полезную работу, существуют методы, которые менеджерят самими объектами. Среди них выделяют так называемые "специальные" методы классов, которые компилятор неявно за вас сгенерирует. Давайте перечислим их и поясним, почему компилятор способен вообще для них написать код:
1️⃣
Конструктор по-умолчанию. Дефолтный конструктор без аргументов. Мы уже подробно разбирали его
в предыдущих постах, останавливаться не будем.
Любые параметрические конструкторы не могут быть специальными методами классов, аля не могут быть сгенерированы компилятором, потому что он не знает, что в общем случае нужно делать с параметрами.
2️⃣
Деструктор. Он семантически разрушает объект. Забирает у него память, закрывает дескрипторы и соединения
, высасывает жизнь, отбирает квартиру, машину, жену и собаку и передает это
государству операционной системе.
Как так получается, что компилятор вообще способен сам сгенерировать код деструктора объекта, который владеет ресурсами?
Все благодаря
RAII и способности деструктора вызывать деструкторы своих полей. Если в вашем классе Foo содержится std::vector или std::unique_ptr, вам не нужно задумываться о менеджменте памяти, авторы этих классов уже все придумали за вас. Вам нужно лишь вызывать их деструктор, что деструктор Foo делает автоматически.
using File = std::unique_ptr<FILE, decltype(&std::fclose)>;
struct Foo {
std::vector<int> numbers;
File file;
// no explicit destructor ~Foo() {}
};
void foo() {
Foo obj = {{1, 2, 3, 4, 5, 6, 7, 8, 9}, {std::fopen("example.txt", "r"), &std::fclose}};
} // destroy obj here
3️⃣
Копирующий конструктор
4️⃣
Перемещающий конструктор
Для этой пары конструкторов правило единое: самое простое решение для копирования/перемещения объекта - это просто скопировать/переместить все его поля в другой объект. Компилятор с этим вполне справится сам: вызовет пачку нужных конструкторов да и все.
Естественно, если хотя бы у одного поля вашего класса не будет копирующего/перемещающего конструктора, сам ваш класс потеряет возможность копироваться/перемещаться.
struct Foo {
std::vector<int> numbers;
// no explicit copy/move constructors
// Foo(const Foo& other) {...}
// Foo(Foo&& other) {...}
};
void foo() {
Foo obj = {{1, 2, 3, 4, 5, 6, 7, 8, 9}};
Foo copied_in_obj = obj; // copy obj
Foo moved_in_obj = std::move(obj); // move from obj
5️⃣
Копирующий оператор присваивания
6️⃣
Перемещающий оператор присваивания
Это операторы, которые вызываются тогда, когда вы хотите передать уже существующему объекту значение другого объекта.
И вот здесь интересная комбинация.
Чтобы присвоить значение, нужно уметь разрушать текущее значение и передавать другое значение. То есть автоматическая генерация присваивания возможно благодаря тому, что компилятор сам умеет разрушать объект и копировать/присваивать значения полей.
Чуть остановимся на синтаксисе:
Foo a, b;
a = b; // copy assign
a = std::move(b); // move assign
Foo a1 = b; // THIS IS NOT AN ASSIGN
Последняя операция не является присваиванием - это вызов конструктора(в этом случае копирующего). Если объект только создается, то это всегда вызов конструктора.
Теперь примерчик:
struct Foo {
std::vector<int> numbers;
// no explicit copy/move constructors
// Foo& operator=(const Foo& other) {...}
// Foo& operator=(Foo&& other) {...}
};
void foo() {
Foo forward = {{1, 2, 3, 4, 5, 6, 7, 8, 9}};
Foo backward = {{9, 8, 7, 6, 5, 4, 3, 2, 1}};
Foo obj; // created obj here
obj = forward; // copy assign forward to obj
obj = std::move(backward); // move assign backward to obj
Итого:
всего 6 специальных методов класса. Не больше, не меньше. На собесах любят про это спрашивать, так что забирайте.
Be special. Stay cool.
#cppcore #cpp11 #interview
Обсуждение 8
Обсуждение не доступно в веб-версии. Чтобы написать комментарий, перейдите в приложение Telegram.
Обсудить в Telegram