4.3 Конструкторы и деструкторы класса
Любой переменной, участвующей в работе программы, требуется память и некоторое
начальное значение. Для переменных встроенных типов размещение в памяти обеспечивается
компилятором. Для локальных переменных память выделяется из стека программы
и занимается для хранения значения данной переменной до тех, пока не закончится
время ее жизни. Сложные типы данных также должны размещаться в памяти и уничтожаться
когда их время жизни закончилось. Это осуществляется с использованием конструкторов
и деструкторов.
Конструктор (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()
{
m_R1=0.;
m_R2=0.;
m_d=0.;
m_D=0.;
m_n=1.;
}
При создании любого экземпляра класса вызывается конструктор. Если при описании
экземпляра не указываются никакие параметры – вызывается конструктор по умолчанию:
// вызов конструктора по умолчанию
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)
{
m_R1=r1;
m_R2=r2;
m_d=d;
m_D=D;
m_n=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)
{
m_d=2.;
m_D=5.;
m_n=1.5;
}
или такой вариант:
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/el_books/programming
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
// проверка на повторное подключение файла
#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/el_books/programming
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
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<m_rows*m_cols; i++)
{
m_data[i] = 0.;
}
}
/////////////////////////////////////////////////////////////////////////////
//деструктор
Matrix::~Matrix()
{
if(m_data != NULL)
{
delete [] m_data;
}
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Программирование на языке высокого уровня. Основы языка С++
// Пример 4.4. Класс Матрица
// test_matrix.cpp
//
// http://aco.ifmo.ru/el_books/programming
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
// подключение описания класса
#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<<error.what()<<endl;
}
...
Если при выполнение какого-то оператора из блока try возникает исключение
– управление сразу переходит к блоку catch. В блоке catch в скобках указывается
тип исключения (exception это наиболее общий вид исключения, возможны и другие
типы) и имя исключения. Внутри блока catch необходимо обработать ошибку.
В нашем случае мы просто выводим на экран сообщение, в каких-то случаях потребуется
более сложная обработка. Функция what() содержит текст, сгенерированный в
момент создания исключения.
В результаты выполнения данного блока программы на экран выведется сообщение " Index
of refraction should be greater than 1.".
Если никаких исключений в try-блоке не происходит, программа игнорирует
его catch-обработчик.
Если исключение было сгенерировано, но перехватывание исключения в блоке
try не происходило, функция, содержащая этот оператор, немедленно завершается,
и программа пытается найти охватывающий try-блок в вызывающей функции. Если
нигде в вызывающих функциях не найдется блок try, программа прервется с сообщением
об ошибке.
|