Что такое конструктор и деструктор c
Любой переменной, участвующей в работе программы, требуется память и некоторое начальное значение. Для переменных встроенных типов размещение в памяти обеспечивается компилятором. Для локальных переменных память выделяется из стека программы и занимается для хранения значения данной переменной до тех, пока не закончится время ее жизни. Сложные типы данных также должны размещаться в памяти и уничтожаться когда их время жизни закончилось. Это осуществляется с использованием конструкторов и деструкторов.
Конструктор (constructor) — это функция-член, имя которой совпадает с именем класса, инициализирующая переменные-члены, распределяющая память для их хранения (new).
// конструктор по умолчанию Lens(); // полный конструктор Lens(double r1, double r2, double D, double d, double n); // конструктор копирования Lens(const Lens& one);
Деструктор (destructor) — это функция-член, имя которой представляет собой ~имя класса, предназначенная для уничтожения переменных (delete).
~Lens(); // деструктор
Одной из особенностей конструктора и деструктора является то, что в отличие от всех остальных функций, у них нет возвращаемого значения.
4.3.1. Конструкторы
Конструктор по умолчанию
Конструктор, не требующий аргументов, называется конструктором по умолчанию. Конструктор по умолчанию не имеет аргументов и инициализирует все переменные члены какими-либо начальными значениями.
// описание конструктора по умолчанию Lens(); // реализация конструктора по умолчанию Lens::Lens()
При создании любого экземпляра класса вызывается конструктор. Если при описании экземпляра не указываются никакие параметры – вызывается конструктор по умолчанию:
// вызов конструктора по умолчанию Lens test_lens1; Lens test_lens2(); Lens test_lens3[10][10];
Полный конструктор
Полный конструктор позволяет явно инициализировать все переменные-члены класса.
// описание полного конструктора Lens(double r1,double r2,double D, double d, double n); // реализация полного конструктора Lens::Lens(double r1, double r2, double D, double d, double n)
Если при описании экземпляра класса в скобках указать параметры, при создании экземпляра класса будет вызван полный конструктор и переменные-члены инициализируются указанными значениями.
// вызов полного конструктора Lens test_lens(10., -10., 2., 5., 1.5);
Неполный конструктор
Возможен и неполный конструктор, когда в списке параметров указываются не все возможные параметры для инициализации членов класса, а только наиболее часто используемые. Остальные параметры будут инициализированы значениями по умолчанию.
// описание неполного конструктора Lens(double r1, double r2); // реализация неполного конструктора Lens::Lens(double r1, double r2) < m_R1=r1; m_R2=r2; m_d=0.; m_D=0.; m_n=1.; >// вызов неполного конструктора Lens lens(10., -10.);
Инициализация переменных-членов класса в конструкторах может осуществляться не только в теле конструктора, но и после оператора :. При этом, во время присваивания переменной-члену значения, будет вызываться не оператор присваивания, а конструктор. Для встроенных типов данных, таких как double или int, это не существенно, но если членами класса являются абстрактные типы, вызов конструктора вместо оператора присваивания будет выполняться быстрее.
Lens::Lens(double r1, double r2) : m_R1(R1) , m_R2(R2)
или такой вариант:
Lens::Lens(double R1, double R2) : m_R1(R1) , m_R2(R2) , m_d(2.) , m_D(5.) , m_n(1.5)
Конструктор копирования
Конструктор копирования создает копию уже существующего экземпляра класса, копируя поэлементно переменные-члены. Конструктор копирования также используется при передаче экземпляров класса в функции по значению. Обратите внимание, что экземпляр класса передается в конструктор по константной ссылке.
// описание конструктора копирования Lens(const Lens& l); // реализация конструктора копирования Lens::Lens(const Lens& l) : m_R1(l.m_R1) , m_R2(l.m_R2) , m_d(l.m_d) , m_D(l.m_D) , m_n(l.m_n) < >// вызов конструктора копирования Lens lens1(10., -10.); Lens lens2(lens1);
4.3.2. Деструктор (пример 4.4. Конструктор и деструктор класса Матрица)
Деструктор осуществляет освобождение памяти, например уничтожение объектов размещенных динамически.
В классе Lens никакого динамического размещения не происходило, поэтому деструктор будет пустой, но его наличие все равно обязательно. Для примера реализации деструктора, представим, что имеется класс Matrix, который в конструкторе динамически создает двумерный массив размерности n x m. Тогда деструктор должен освобождать память, которую выделяет конструктор.
Конструктор вызывается в момент создания переменной, деструктор вызывается когда время жизни переменной закончилось, то есть когда встречается закрывающая фигурная скобка > блока, в которой была объявлен экземпляр класса, либо когда вызывается оператор delete при динамическом размещении экземпляра класса.
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.4. Класс Матрица // matrix.h // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// // проверка на повторное подключение файла #if !defined MATRIX_H #define MATRIX_H ///////////////////////////////////////////////////////////////////////////// // класс Матрица class Matrix < private: //число строк и число столбцов int m_rows, m_cols; //указатель на динамический массив данных double* m_data; public: //конструктор по умолчанию Matrix(); //полный конструктор Matrix(int rows, int cols); //деструктор ~Matrix(); // . >; ///////////////////////////////////////////////////////////////////////////// #endif //defined MATRIX_H
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.4. Класс Матрица // matrix.cpp // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// #include using namespace std; // подключение описания класса #include "matrix.h" ///////////////////////////////////////////////////////////////////////////// // конструктор по умолчанию Matrix::Matrix() : m_rows(0) , m_cols(0) , m_data(NULL) < >///////////////////////////////////////////////////////////////////////////// // полный конструктор Matrix::Matrix(int rows, int cols) : m_rows(rows) , m_cols(cols) < m_data=new double [rows*cols]; for(int i=0; i > ///////////////////////////////////////////////////////////////////////////// //деструктор Matrix::~Matrix() < if(m_data != NULL) < delete [] m_data; > > /////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// // Прикладное программирование // Пример 4.4. Класс Матрица // test_matrix.cpp // // Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru // Университет ИТМО ///////////////////////////////////////////////////////////////////////////// // подключение описания класса #include "matrix.h" ///////////////////////////////////////////////////////////////////////////// void main() < // в момент создания матрицы вызывается конструктор по умолчанию Matrix a; // в момент создания матрицы 3х3 вызывается полный конструктор Matrix b(3,3); // . >// время жизни переменных a и b заканчивается, вызывается деструктор /////////////////////////////////////////////////////////////////////////////
4.3.3. Проверка правильности параметров. Исключительные ситуации
Конструкторы должны проверять передаваемые им аргументы на корректность значений. Например, показатель преломления не может быть меньше 1. Что делать, если в конструктор были переданы неправильные параметры? Для этого в языке С++ существуют исключительные ситуации.
Класс exception является стандартным базовым классом C++ для всех исключений. Исключения можно сгенерировать в случае возникновения непредвиденной ошибки, например мы предполагаем что при вызове класса Lens никто не будет пытаться задать показатель преломления меньше 1, но при этом такая ситуация возможна, и это может привести к ошибке. Сгенерировать исключительную ситуацию можно при помощи оператора throw:
if(n <1) throw exception("Index of refraction should be greater than 1.");
Для обработки возникшей исключительной ситуации используются try и catch блоки.
В блок try заключается код, в котором предположительно могут возникнуть исключительные ситуации. В нашем случае это вызов конструктора. Кроме того, в этот же блок заключают операторы, которые должны быть пропущены в случае, если исключение возникает. В нашем случае вычисление и вывод на экран параксиальных характеристик не имеет смысл выполнять, если в конструкторе возникла ошибка.
// полный конструктор Lens::Lens(double r1,double r2,double D,double d,double n) : m_r1(r1) , m_r2(r2) , m_d(d) , m_D(D) , m_n(n) < if(n<1) throw exception("Index of refraction should be greater than 1."); CalculateParaxial(); > . //---------------------------------------------------------------- // в случае возникновения исключительной ситуации внутри блока try // управление переходит к блоку catch try < Lens lens7(100., -100., 50., 5., 0.); parax=lens7.GetParaxial(); parax.write(cout); >// блок catch - обработка ошибки catch(exception& error) < // вывод на экран сообщения об ошибке cout .
Если при выполнение какого-то оператора из блока try возникает исключение – управление сразу переходит к блоку catch. В блоке catch в скобках указывается тип исключения (exception это наиболее общий вид исключения, возможны и другие типы) и имя исключения. Внутри блока catch необходимо обработать ошибку. В нашем случае мы просто выводим на экран сообщение, в каких-то случаях потребуется более сложная обработка. Функция what() содержит текст, сгенерированный в момент создания исключения.
В результаты выполнения данного блока программы на экран выведется сообщение » Index of refraction should be greater than 1.».
Если никаких исключений в try-блоке не происходит, программа игнорирует его catch-обработчик.
Если исключение было сгенерировано, но перехватывание исключения в блоке try не происходило, функция, содержащая этот оператор, немедленно завершается, и программа пытается найти охватывающий try-блок в вызывающей функции. Если нигде в вызывающих функциях не найдется блок try, программа прервется с сообщением об ошибке.
Конструктор и деструктор класса в C++
Возможно вы заметили, что определяя класс, мы не можем инициализировать его поля (члены) в самом определении. Можно присвоить им значение, написав соответствующий метод класса и вызвав его, после создания объекта вне класса. Такой способ не совсем удобен, так как объявляя, допустим, 33 объекта класса нам придется 33 раза вызывать метод, который присваивает значения полям класса. Поэтому, как правило, для инициализации полей класса, а так же для выделения динамической памяти, используется конструктор.
Конструктор (от construct – создавать) – это особый метод класса, который выполняется автоматически в момент создания объекта класса. То есть, если мы пропишем в нем, какими значениями надо инициализировать поля во время объявления объекта класса, он сработает без “особого приглашения”. Его не надо специально вызывать, как обычный метод класса.
using namespace std ;
class SomeData
int someNum1 ;
double someNum2 ;
char someSymb [ 128 ] ;
SomeData ( ) //это конструктор:
someNum1 = 111 ; // присваиваем начальные значения полям
someNum2 = 222.22 ;
strcpy_s ( someSymb , «СТРОКА!» ) ;
cout << "\nКонструктор сработал!\n" ;
void showSomeData ( )
> obj1 ; //уже на этом этапе сработает конструктор (значения запишутся в поля)
setlocale ( LC_ALL , «rus» ) ;
obj1 . showSomeData ( ) ;
SomeData obj2 ; //тут сработает конструктор для второго объекта класса
obj2 . showSomeData ( ) ;
В строках 11 – 17 определяем конструктор: имя должно быть идентично имени класса; конструктор НЕ имеет типа возвращаемого значения (void в том числе). Один объект объявляется сразу во время определения класса – строка 25. При запуске программы, конструктор этого объекта сработает даже до входа в главную функцию. Это видно на следующем снимке:
программа еще не дошла до выполнения строки 29 setlocale ( LC_ALL , «rus» ) ; , а конструктор уже “отчитался”, что сработал (кириллица отобразилась некорректно). В строке 30 – смотрим, что содержат поля класса. Второй раз конструктор сработает в строке 32, во время создания объекта obj2 .
Деструктор (от destruct – разрушать) – так же особый метод класса, который срабатывает во время уничтожения объектов класса. Чаще всего его роль заключается в том, чтобы освободить динамическую память, которую выделял конструктор для объекта. Имя его, как и у конструктора, должно соответствовать имени класса. Только перед именем надо добавить символ ~
Добавим деструктор в предыдущий код. И создадим в классе два конструктора: один будет принимать параметры, второй – нет.