Поскольку классы имеют сложную внутреннюю структуру, включая сложные хранимые данные, инициализация и очистка объектов для классов намного сложнее, чем для простых структур данных. Конструкторы и деструкторы - это специальные функции-члены классов, которые используются для создания и уничтожения объектов класса. Создание объектов может включать в себя выделение памяти и инициализацию полей объектов. Уничтожение может включать очистку и освобождение памяти для объектов.
Как и другие функции-члены, конструкторы и деструкторы объявляются в объявлении класса. Они могут быть определены как встроенные, так и внешние по отношению к объявлению класса. Конструкторы могут иметь аргументы по умолчанию. В отличие от других функций-членов, конструкторы могут иметь списки инициализации членов. К конструкторам и деструкторам применяются следующие ограничения:
- Конструкторы и деструкторы не имеют возвращаемых типов и не могут возвращать значения.
- Не возможно взять указатель или ссылку на конструктор или деструктор.
- Конструкторы не могут быть объявлены с ключевым словом virtual.
- Конструкторы и деструкторы не могут быть объявлены static, const или volatile.
- Union не могут содержать объекты класса, у которых есть конструкторы или деструкторы.
Пример #1: Невозможность ссылки на конструктор и деструктор
class A
{
private:
int x;
public:
A() { x = 100; };
void* member() { x = 200; };
};
template <class X>
void call_me(A s, X function){
(s.*function)();
};
call_me(s, &A::member); //правильно
call_me(s, &A::A); //неправильно
call_me(s, &A::~A); //неправильно
Конструкторы и деструкторы подчиняются тем же правилам доступа, что и функции-члены. Например, если вы объявляете конструктор с protected доступом, только производные классы и friend классы могут использовать его для создания объектов класса.
Пример #2: Унаследованные классы и классы друзья
class C;
class A
{
protected:
A() {}
private:
friend C;
};
class B: public A
{
public:
B(): A() {}
};
class C
{
public:
void member()
{
A a; //правильно
}
};
int main(int argc, char** argv)
{
A a; //неправильно
B b; //правильно
return 0;
}
Компилятор автоматически вызывает конструкторы при определении объектов класса и вызывает деструкторы, когда объекты класса выходят за пределы области видимости. Если для объектов требуется выделение памяти, конструкторы могут явно вызвать оператор new. Во время очистки деструктор может освободить объекты, выделенные соответствующим конструктором. Чтобы освободить объекты, используйте оператор delete.
Производные классы не наследуют и не перегружают конструкторы или деструкторы из своих базовых классов, но они вызывают конструктор и деструктор базовых классов. Деструкторы могут быть объявлены с помощью ключевого слова virtual. Для классов которые должны быть унаследованы, обязательно объявлять деструктор с помощью ключевого слова virtual.
Конструкторы также вызываются при создании локальных или временных объектов класса, а деструкторы вызываются, когда локальные или временные объекты выходят за пределы области видимости.
Пример #3: Создание и удаление объекта при изменении областей видимости
#include <iostream>
class C
{
public:
C(){ std::cout << "C::C() Constructor" << std::endl; }
~C(){ std::cout << "C::~C() Destructor" << std::endl; }
};
int main(int argc, char** argv)
{
std::cout << "Beggining of default scope" << std::endl;
{
std::cout << "Begginging of anonymous scope" << std::endl;
C c;
std::cout << "End of anonymous scope" << std::endl;
}
std::cout << "End of default scope" << std::endl;
return 0;
}
Вывод:
Beggining of default scope
Begginging of anonymous scope
C::C() Constructor
End of anonymous scope
C::~C() Destructor
End of default scope
Вы можете вызывать функции-члены из конструкторов или деструкторов. Вы можете вызвать виртуальную функцию, прямо или косвенно, из конструктора или деструктора класса A. В этом случае вызываемая функция - это функция, определенная в A или классе родителе A, но не функция, переопределенная в любом унаследованном от A классе. Это позволяет избежать возможности доступа к не полностью созданному объекту из конструктора или деструктора. Следующий пример демонстрирует это:
#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "void A::f()" << endl; }
virtual void g() { cout << "void A::g()" << endl; }
virtual void h() { cout << "void A::h()" << endl; }
};
struct B : A {
virtual void f() { cout << "void B::f()" << endl; }
B() {
f();
g();
h();
}
};
struct C : B {
virtual void f() { cout << "void C::f()" << endl; }
virtual void g() { cout << "void C::g()" << endl; }
virtual void h() { cout << "void C::h()" << endl; }
};
int main(int argc, char** argv) {
C object;
return 0;
}
Вывод:
void B::f()
void A::g()
void A::h()
Конструктор B не вызывает ни одну из функций, переопределенных в C, поскольку C был унаследован от B, хотя в примере создается объект типа C с именем object.