Конструкторы и деструктор класса
Когда мы работали со структурами, мы могли выполнить их инициализацию при создании, для классов тоже существует подобная возможность, но в несколько расширенном виде.
Примечание: в дальнейшем в статье словом «специфический» обозначено нестандартное выделение/выделение не по умолчанию / работа с динамической памятью и тд
- Как было сказано в более ранних статьях, классы – своего рода шаблоны, по которым собираются объекты/переменные класса, а у всех переменных есть зона видимости, т.е. зона где переменная «создается->существует->удаляется» для простых типов данных, хранящихся на стеке это относительно просто, они удаляются автоматически (об этом заботится компилятор), для динамической памяти в языке C++ выделение и уничтожение памяти лежит на программисте.
Значит процесс создания класса и удаление должны быть «специфическими». - Также стоит сказать, что создание неинициализированного класса, как и создание неинициализированной переменной может приводить к ошибкам (например переменная string, которая пустая, или int, который не инициализирован 0).
Следовательно, нужен механизм для создания и уничтожения классов (точнее объектов классов), таким механизмом являются конструкторы и деструкторы.
Сейчас при создании класса компилятор подставляет конструктор и деструктор по умолчанию (они ничего «специфического» не делают).
Внешне они выглядят так:
class myClass{
myClass(){}//конструктор
~myClass(){}//деструктор
};
Расмотрим реализацию конструктора на примере:
Возьмем наш класс Person, где поле age будет расположено в динамической памяти.
class Person
{
public:
Person(string Name, unsigned int age)
{
this->Name=Name;
this->age= new unsigned int (age); //создание переменной в динамической памяти
}
~Person()
{
delete age; //удаление переменной в динамической памяти
}
void getInfo()
{
cout<<"Name: "<< this->Name <<" age "<< *(this->age) <<endl; //выводим данные на экран
}
private:
string Name;
unsigned int * age; //указатель на переменную в динамической памяти
};
В функции main:
//Создадим объект класса Person и проинициализируем его поля.
Person c_Person1("Vasya",30);
c_Person1.getInfo(); //Выведет в консоль Name Vasya age 30
Что же мы сделали:
1-вызвали конструктор (считай что функцию инициализации) Person(string Name, unsigned int age), в которую передали параметры, и внутри нее создали переменную в динамической памяти.
p.s. переменную создавали внутри просто для простоты, можно передавать указатель и через аргументы конструктора.
2-После мы можем работать с классом например вызвать метод getInfo().
3-Вот тут кроется интересный момент, переменные string Name; и указатель unsigned int age; расположены на стеке, поэтому они удаляются автоматически, а данные, на которые ссылается указатель останутся.
При выходе класса из зоны видимости (например функции/цикла и тд) очистится память в стеке, содержащая указатель на age, и при отсутствии деструктора произойдет утечка, но мы написали деструктор ~Person() в котором освободили динамическую память.
Теперь при уничтожении объекта класса будет вызван деструктор и память освободится.
Важный момент: конструктор, как и любой метод или функцию можно перегрузить – т.е. написать несколько реализаций, деструктор может быть ТОЛЬКО ОДИН