Сохраняемость. Модификаторы и селекторы и другие члены классов

Модификаторы и селекторы и другие члены классов

Классы, их конструкторы и деструкторы

Структуры

Примером гетерогенного типа данных может служить структура -- объединение нескольких компонентов в переменную с одним именем.

// структура для работы с компонентами цвета
struct color
{
unsigned int red, green, blue;
};

Создавая структуру, вы создаете новый тип данных, который можно использовать также как и стандартные, встроенные типы данных.

color x; // объявление составной переменной

x.red=255; // инициализация переменных-членов структуры
x.green=255; // для доступа к членам используется оператор
x.blue=255; // переменная для хранения цвета инициализирована белым цветом

Гетерогенные типы данных также можно размещать статически и динамически.

color* x; // динамическое размещение структуры в памяти
x=new rgb;

color->red=0; // оператор -> оператор доступа к членам структуры
color->green=0; // по указателю на нее
(*color).blue=0; // можно использовать оператор разыменования

Также, предусмотрены операторы для получения указателя на член класса

unsigned int* p=x.*m_re; // получение указателя на член класса
unsigned int* p=x->*green; // получение указателя на член класса

Гетерогенные типы данных несложно передать в функцию (как по значению, так и по указателю, и по ссылки). Гетерогенные типы данных по сути дела являются абстрактными типами данных, которые при желании можно максимально приблизить к поведению встроенных типов.

unsigned int luminance(color& c)
{
return 0.212671 * c.red + 0.715160 * c.green + 0.072169 * c.blue;
}

Гетерогенные типы данных можно снабдить своими собственными функциями для обработки своего содержимого. Например функцию luminance можно сделать членом структуры color:

// структура для работы с компонентами цвета
struct color
{
unsigned int red, green, blue;
unsigned int luminance()
{
return 0.212671 * red + 0.715160 * green + 0.072169 * blue;
}
};

Теперь для вызова этой функции-члену этой структуры нужно использовать операторы доступа. или ->, так же как и для доступа к членам-переменным:

unsigned int l=x.luminance();

При этом экземляр x структуры color передаётся в функцию luminance автоматически.

В языке С/С++ концепция классов стала расширением понятия структуры, но в отличие от структуры, как гетерогенного типа данных (предназначенного для хранения разнородных переменных), классы предназначены для выражения не только структуры объекта, но и его поведения. То есть благодаря классам мы можем описать объект (его атрибуты и поведение) на языке программирования и заставить его "жить" в программе. Атрибуты будут описаны на языке в виде переменных-членов класса, а поведение (операции, действия) - в виде функций-членов.

Также как для идентификаторов (переменных) и функций существуют понятия определения, объявления, реализации класса.

Объявление класса - резервирует имя (лексему) как имя класса, нового типа данных.

class Lens;
class Beam;
class Surface;

Определение класса - языковая конструкция, которая определяет переменные и функции члены класса (их имена, набор аргументов).

class Lens
{
private:
// переменные-члены:
float m_R1,m_R2;
float m_D,m_d;
float m_n;
public:
// функции-члены:
Lens(); // конструктор по умолчанию
Lens(float r1,float r2,float D, float d, float n); // полный конструктор
Lens(Lens& one); // конструктор копирования
~Lens(); // деструктор
...
void setn(float n); // inline
float getn() const; // постоянная функция-селектор
...
int getType() const; // постоянная функция-селектор
float getf() const; // постоянная функция-действие
...
};

Любой переменной участвующей в работе программы, требуется память и некоторое начальное значение. Для переменных встроенных типов размещение в памяти обеспечивается компилятором. Для локальных переменных память выделяется из стека программы и занимается для хранения значения данной переменной до тех пока не закончится время ее жизни. Сложные типы данных (АТД) также должны размещаться в памяти и уничтожаться когда их время жизни закончилось. Это осуществляется с использованием конструкторов и деструкторов.

Конструктор (constructor) - это функция-член (особая), имя которой совпадает с именем класса, инициализирующая переменные-члены, распределяющая память для их хранения (new).

Деструктор (destructor) - это функция-член (особая), имя которой представляет собой ~имя класса, предназначенная для уничтожения переменных (delete).

Конструктор не требующий аргументов, называется конструктором по умолчанию.

Lens::Lens() // конструктор по умолчанию не имеет аргументов и инициализирует
// все переменные члены какими-либо начальными значениями
{
m_R1=10.f; m_R2=-10.f; m_d=2.f; m_D=5.f; m_n=1.5f;
}

Lens l; // вызовет конструктор по умолчанию
Lens l(); // тоже
Lens l[10]; //при определении массивов вызываются конструкторы по умолчанию

Полный конструктор позволяет явно инициализировать все переменные-члены класса

Lens::Lens(float r1,float r2,float D, float d, float n)
{
m_R1=r1; m_R2=r2;
m_d=d; m_D=D; m_n=n;
}

Lens l(10.f,-10.f,2.f,5.f,1.5f); // вызовет полный конструктор и инициализирует переменные-члены указанными значениями

Конструкторы должны проверять передаваемые им аргументы на корректность значений.

Инициализация переменных-членов класса в конструкторах может осуществляться также следующим образом:

Lens::Lens(float R1, float R2)
:m_R1(R1), m_R2(R2)
{
m_d= 2.f; m_D= 5.f;
m_n= 1.5f;
}

Конструктор копирования - осуществляет поэлементное копирование переменных-членов класса, создает копию уже существующего класса. Конструктор копирования также используется при передаче экземпляров класса в функции по значению.

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;
}

Деструктор - осуществляет освобождение памяти, уничтожение объектов размещенных динамически

Matrix::Matrix(int r, int c)
{
m_data=new float[r*c];
}

Matrix::~Matrix()
{
delete []m_data;
}

inline - встраиваемая функция (отдельный код) компилятор не генерирует, а подставляет ее код в каждое место вызова.

inline void Lens::setn(float n) //:: определяет принадлежность к классу
{
m_n=n; // изменяет значение переменной-члена
}

const - постоянная функция не изменяет значений переменных членов (обычно это селектор)

float Lens::getn() const
{
return m_n; // не изменяет значение переменной-члена
}

Функции модификаторы состояния (setR1(), setR2(), setD(), setd(), setn()) были определены как невозвращающие значения. Но при использовании подобных функций иногда удобно выстроить операции их вызова в одну цепочку:

l.setR1(10.f).setR2(-10.f).setD(5.f).setd(2.f).setn(1.5f);

Для этого нужно, чтобы каждая из этих функций возвращала ссылку на модифицированный экземпляр класса Lens.

Lens& Lens::setn(float n)
{
m_n=n; // то же самое, что и this->m_n=n
return *this;
}

где this - указатель на данный экземпляр класса. Данный, т.е. для которого вызвана эта функция-член. Часто используется косвенно.

Любой программный объект существует в определенный промежуток времени (пока работает программа, пока он находится в памяти). Сохраняемость - способность объекта существовать во времени, переживая породивший его процесс, и в пространстве, перемещаясь из своего первоначального адресного пространства. Сохранение информации о состоянии объекта, с последующей возможностью его восстановления называется сериализацией. Без чтения исходных данных и записи результатов не обойтись ни в одной программе. Принцип сохраняемости обобщает эти процессы. В зависиости от реализации сериализации состояние объекта может быть сохранено в памяти, в файле, в базе данных и т.п. и в любой момент восстановлено. Например:

void Lens::write(ostream& out) const
{
cout<<m_R1<<m_R2<<m_d<<m_D<<m_n;
}
void Lens::read(istream& in)
{
cin>>m_R1>>m_R2>>m_d>>m_D>>m_n;
}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: