double arrow

Поведение


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

Поведение – это то, как объект действует и реагирует; поведение выра­жается в терминах состояния объекта и передачи сообщений. Поведение объекта – это его наблюдаемая и проверяемая из­вне деятельность.

Операция – это услуга, которую можно запросить у любого объекта класса для воздействия на его поведение.

Например, клиент может активизиро­вать операции push и pop для того, чтобы управлять объектом-стеком (доба­вить или изъять элемент).

В чисто объектно-ориентированном языке принято говорить о передаче сооб­щений между объектами. В C++ мы говорим, что один объект вызывает функцию-член другого. В основном понятие «сообщение» совпадает с понятием «операция над объектами».

Передача сообщений – это один уровень, задающий поведение. Из нашего определения следует, что состояние объекта также влияет на его поведе­ние.

Рассмотрим торговый автомат. Мы можем сделать выбор, но поведение авто­мата будет зависеть от его состояния. Если мы не опустили в него достаточную сумму, скорее всего, ничего не произойдет. Если же денег достаточно, автомат вы­даст нам желаемое (и тем самым изменит свое состояние).




Некоторые операции изменяют состояние. В связи со сказанным можно заключить, что состояниеобъекта представляет суммарный результат его поведения.

Операция – это услуга, которую класс может предоставить своим кли­ентам. На практике типичный клиент совершает над объектами операции следующих ви­дов:

– модификатор – это операция, которая изменяет состояние объекта (например, set-функция);

селектор – это операция, считывающая состояние объекта, но не меня­ющая состояния (например, get-функция);

– конструктор – это операция создания объекта и/или его инициализации; в С++ конструктор имеет то же имя, что и класс;

деструктор – это операция, освобождающая ресурсы, которые использует объект, и/или разрушающая сам объект; в С++ имя деструктора состоит из имени класса, перед которым ставится знак «тильда» – «~».

Две последние операции являются универсальными. Они обеспечивают инфраструктуру, необходимую для создания и уничтожения экземпляров класса. Если у класса есть конструктор, то он вызывается всегда, когда создается объект класса. Если у класса есть деструктор, то он вызывается всегда, когда объект класса уничтожается. Если программист не описал в классе конструктор и деструктор, то они будут созданы автоматически.

Объекты могут создаваться следующим образом:

автоматический объект создается каждый раз, когда его описание встречается при выполнении программы, и уничтожается каждый раз при выходе из блока, в котором оно появилось;



статический объект создается один раз, при запуске программы, и уничтожается один раз, при ее завершении;

объект в свободной памяти создается с помощью операции new и уничтожается с помощью операции delete;

объект-член создается как подобъект другого класса.

Пример. Расширим описание класса Stack, с тем чтобы программист мог задавать максимальный размер каждого создаваемого стека (размер массива s).

class Stack {

int *s, length, top;

. . .

public:

Stack(int n = 100){ // конструктор, n – максимальный размер,

// значение максимального размера по умолчанию – 100

length = n; s = new int [length]; top = 0;}

~Stack( ) { delete [ ] s; } // деструктор

void push(const int el); // модификатор

int pop( ); // модификатор

bool isFull( ) const; // селектор

bool isEmpty( ) const; // селектор

. . .

};

Теперь мы можем объявить нужные нам стеки:

int len=100;

Stack st1(len), st2(200);

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

Пример. Рассмотрим определение класса complex.

class complex {

double re, im;

public:

complex(double r, double i);

complex(double r);

. . .

};

Мы определили два конструктора, один из которых имеет один аргумент и служит для инициализации комплексного числа (его действительной части) значением вещественного числа. Теперь мы можем записать два эквивалентных оператора

complex a = complex(1);

complex a = 1;

Последнее присваивание имеет однозначный смысл с точки зрения предметной области. В то же время для стека аналогичное присваивание



Stack st = 100;

будет неоднозначным по смыслу и может быть потенциальным источником ошибок.

Можно запретить использование конструктора для таких преобразований, объявив его с ключевым словом explicit.

class Stack {

public:

explicit Stack(int n = 100); // конструктор, задающий максимальный

. . . // размер стека, нельзя использовать для преобразования

};

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

Важно также, что такая инициализация выполняется эффективнее, поскольку создание объекта в C++ начинается с инициализации его атрибутов конструктором по умолчанию, после чего выполняется вызываемый конструктор. Использование инициализаторов позволяет сразу же вызвать нужный конструктор.

Для инциализаторов используется синтаксис следующего примера:

class Id_Stack {

const int id; Stack s;

public: Id_Stack(int i, int n): id(i), s(n){ };

};

В чисто объектно-ориентированных языках определять процедуры и функции вне классов не допускается. В гибридных языках, выросших из процедурных языков, таких как C++, допускается описывать операции как независимые от объектов подпрограммы.

Операции, определенные вне классов, называют сво­бодными подпрограммами. В C++ они называются функциями-нечленами.

bool check_stack(Stack & my_stack, int el)

{

Stack temp_stack;

. . . // используя дополнительный стек temp_stack, проверить,

//есть ли в my_stack элемент el

}

Свободные подпрограммы – это процедуры и функции, которые выполняют роль операций высокого уровня над объектом или объектами одного или разных классов. Свободные подпрограммы обычно группируются в соответствии с классами, для которых они создаются.







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