Манипуляции с состоянием объекта 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Манипуляции с состоянием объекта



 

Для доступа к внутренним информационным членам объекта, созданного на основе класса (чтение/запись), необходимо использовать специальные методы класса, называемые модификаторами (setters) и селекторами (getters). Они осу-ществляют подконтрольное считывание и изменение внутренних информаци-онных членов. Так, если изменяется внутреннее информационное поле size объекта класса stack, описывающее максимальный размер стека, то необходимо осуществить ряд действий по согласованному изменению других информаци-онных членов (выделение дополнительной памяти и т. д.):

 

class stack { int * c1;

int top, size; public:

 

...

 

int putnewsize(int ns){ if (top > ns) return 1; int * nc1 = new int [ns]; if (top > 0)

 

for (int i = 0; i < top; i++)nc1[i] = c1[i];

 

delete c1;c1 = nc1; size = ns; return 0;

 

}

 

};

 

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

 

Класс как область видимости

 

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

 

Операция '::' позволяет получить доступ к одноименным объектам, внешним по отношению к текущей области видимости, в частности, к глобальным функциям и переменным, следующим образом:

 

имя_класса:: имя_члена_класса или

 

:: имя - для имен глобальных функций и переменных.

 

Пример:

 

int ia1;

 

void f1(int b1) {ia1 = ia1 + b1;

 

}

 

class x {

 

int ia1; public:

 

x(){ia1 = 0;} void f1(int b1){

 

::f1(b1); // вызов глобальной функции

 

}

 

};

 

int main(){x a2; a2.f1(2); return 0;

 

}

 

3.6. Объявление и определение методов класса. Спецификатор inline

 

Каждый метод класса, должен быть определен в программе. Определить метод класса можно либо непосредственно в классе (если тело метода не слишком сложно и громоздко), либо вынести определение вне класса, а в классе только объявить соответствующий метод,указав его прототип.

 

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

 

Пример:

 

class x {

 

int ia1; public:

 

x(){ia1 = 0;} int func1();

};

 

int x::func1(){ … return ia1; }

 

Это позволяет повысить наглядность текста, особенно, в случае значитель-ного объема кода в методах. При определении метода вне класса с использова-нием операции ‘::’ прототипы объявления и определения функции должны совпадать.

 

Метод класса и любую функцию, не связанную ни с каким классом, можно определить со спецификатором inline:

 

inline int func1();

 

Такие функции называются встроенными.

 

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

 

По определению методы класса, определенные непосредственно в классе, являются inline -функциям

 

3.7. Указатель this

 

В классах С++ неявно введен специальный указатель this – указатель на текущий объект. Каждый метод класса при обращении к нему получает данный указатель в качестве неявного параметра. Через него методы класса могут по-лучить доступ к другим членам класса.

 

Указатель this можно рассматривать как локальную константу, имеющую тип X*, если X – имя описываемого класса. Нет необходимости использовать его явно. Он используется явно, например, в том случае, когда выходным значением для метода является текущий объект.

Данный указатель, как и другие указатели, может быть разыменован.

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

 

Пример:

 

class x {

 

...

 

public:

 

x& f(...){

 

...

 

return * this;

 

}

 

};

 

Указатель на член класса

 

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

 

class X {

 

 

int i; public:

Х(){i = 1;} int f1(int j){

 

cout << "print i" << i << "\n"; return j;

}

 

int f2(int j){

 

cout << "reset i \n"; i = 1; return j;

}

 

int f3(int j){

 

cout << "set i \n"; i = j; return j;

}

 

};

 

typedef int (Х::* pf)(int); // см. комментарий 1)

 

intmain(){    
intk, sw, par;    
x b; // см. комментарий 2)  
pf ff;  
...    
switch(sw){ // см. комментарий 3)  
case1: ff=&x::f1;  
break;    
case2: ff=&x::f2;    
break;    
case3: ff=&x::f3;    
}; // см. комментарий 4)  
k = (b.*ff)(par);  
...    
return0;    
}    
Комментарии:    

 

1) ключевое слово typedef вводит новое имя для типа:

 

typedef int (Х::* pf)(int);-pf–тип указателя на методкласса X с одним входным параметром типа int и типом возвращаемого значения – int.

 

2) pf ff; – создание объекта ff, имеющего введенный тип p f.

 

3) ff = &Х::f1; – указателю ff присваивается адрес одного из методов класса. Доступ к этому методу по данному указателю через какой-либо объект невозможен (оператор ff = &b.f1; – неверен). Дело в том, что -указатель на член класса представляется для нестатических членов не

 

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

 

4) k =(b.* ff)(par); – разыменование указателя на нестатический метод класса дает доступ к коду по относительному адресу, который применяется к базовому адресу конкретного объекта (в данном случае – объекта b).

 

Примечание. В случае объявления методов статическими членами(см.раздел«Статические члены класса») идентификатор pf необходимо объявить обычным указа-телем на функцию:

 

typedef int (* pf)(int);

 

Разыменование объекта такого типа представляется обычным разыменованием указа-теля на функцию:

 

k = (*ff)(par);

 

Применение техники разыменования указателя на метод класса является проявлением динамического полиморфизма, когда исполняемый код для одного и того же оператора (k = (b.*ff)(par)) определяется на этапе исполнения, а не компиляции. В большей мере динамический полиморфизм реализуется вир-туальными функциями,описываемыми в следующих разделах.

 

Конструкторы и деструкторы

 

Конструкторы и деструкторы являются специальными методами класса Конструкторы вызываются при создании объектов класса и отведении па-

 

мяти под них.

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

 

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

 

Так как конструкторы и деструкторы неявно входят в интерфейс объекта, их следует располагать в открытой области класса.

 

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

 

Отличия и особенности описания конструктора от обычной функции:

 

1) Имя конструктора совпадает с именем класса

 

2) При описании конструктора не указывается тип возвращаемого значения

 

 

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

 

В описании конструктора тип возвращаемого значения не указывается не потому, что возвращаемого значения нет. Оно как раз есть. Ведь результатом работы конструктора в соответствии с его названием является созданный объект того типа, который описывается данным классом. Страуструп отмечал, что кон-структор – это то, что область памяти превращает в объект.

 

Конструкторы можно классифицировать разными способами:

 

1) по наличию параметров:

 

− без параметров,

 

− с параметрами;

 

2) по количеству и типу параметров:

 

− конструктор умолчания,

 

− конструктор преобразования,

 

− конструктор копирования,

 

− конструктор с двумя и более параметрами.

 

Набор и типы параметров зависят от того, на основе каких данных создается объект.

 

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

 

Деструкторы применяются для корректного уничтожения объектов.Частопроцесс уничтожения объектов включает в себя действия по освобождению вы-деленной для них по операциям new памяти.

 

Имя деструктора: ~ имя_класса У деструкторов нет параметров и возвращаемого значения.

 

В отличие от конструкторов деструктор в классе может быть только один.

 

Пример: Описание класса.

 

class box{

 

int len, wid, hei; public:

box(int l, int w, int h){

 

len = l; wid = w; hei = h;

 

}

 

box(int s){

 

len = wid = hei = s;

}

 

box(){

len = 2; wid = hei = 1;

 

}

int volume(){

 

return len * wid * hei;

}

 

};

 

Конструктор умолчания

 

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

 

Если для создания объекта не требуется каких -либо параметров, то исполь-зуется конструктор умолчания. При описании таких объектов после имени класса указывается только идентификатор переменной:

 

class Х{ };

 

Х x1;

 

Замечание:роль конструктора умолчания может играть конструктор,у ко-торого все параметры имеют априорные значения, например:

 

box (int l = 24, int w = 12, int h = 6);

 

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

 

Если для создания объекта необходимы параметры, то они указываются в круглых скобках после идентификатора переменной:

 

box b2(1,2,3); box b3(5);

 

Указываемые параметры являются параметрами конструктора класса. Если у конструктора имеется ровно один входной параметр, который не представляет собой ссылку на свой собственный класс, то соответствующий конструктор на-зывается конструктором преобразования. Этот конструктор называется так в связи с тем, что в результате его работы на основе объекта одного типа создается объект другого типа (типа описываемого класса).

 

Если уже описан класс T и описывается новый класс X, то его конструкторы преобразования могут иметь любой из следующих прототипов:

 

X(T);

 

X(T&);

 

X(const T&);

 

Последний прототип служит для защиты от изменения передаваемого фак-тического параметра в теле конструктора, так как при получении ссылки на фактический параметр используется собственно передаваемый объект, а не его локальная копия.

 

 

Примечание. Выделение в отдельную группу конструкторов с двумя и более па-раметрами, независимо от их типа, является в некотором смысле, условным. Так, на-пример, если есть два класса: Vector и Matrix, то для создания соответствующих объектов:

 

Vector v1(10);

 

Matrix m1(10,15);

 

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

 

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

 

Пример:

 

class X { int x1;

 

public:

 

X(int px1 = 0}

 

};

 

Для такого класса будут верны следующие объявления объектов:

 

int main(){

 

… Х x1, x2(1); …

 

}

Конструктор копирования

 

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

 

Для такого создания объекта используется конструктор копирования. Инициализация может быть выполнена аналогично инициализации пере-

 

менных встроенных типов с использованием операции присваивания совместно с объявлением объекта:

 

box b5(2,4,6); // создание объекта типа box с // использованием числовых данных

box b6 = b5; // создание объекта b6 – копии объекта b5

 

Если инициализация производится объектом такого же типа, то объ-ект-инициализатор также может быть указан в круглых скобках после иденти-фикатора создаваемого объекта:

 

box b7(b5);

 

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

 

Если класс не предусматривает создания внутренних динамических струк-тур, например, массивов, создаваемых с использованием операции new, то в конструкторе копирования достаточно предусмотреть поверхностное копиро-вание,то есть почленное копирование информационных членов класса.

 

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

 

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

 

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

 

Пример: Для класса stack конструктор копирования может быть опреде-лен следующим образом:

 

class stack { char * c1;

int top, size; public:

stack(int n = 10){ size = n;

top = 0;

c1 = new char [size];

 

}

 

stack(stack & s1);

...

};

 

stack::stack(stack & s1){ size = s1.size;

 

top = s1.top;

 

c1 = new char [size];

 

for (int i = 0; i < size; i++)c1[i] = s1.c1[i];

 

}

 

Замечания по работе конструктора копирования:

 

1) Входной параметр является внешним объектом по отношению к созда-

 

ваемому объекту. Тем не менее, имеется возможность прямого обраще-ния к закрытым членам этого внешнего объекта. Это возможно только потому, что входной параметр имеет тип, совпадающий с типом созда-ваемого в результате работы конструктора копирования объекта. Если бы на вход конструктора поступал бы объект другого типа (например, в конструкторе преобразования класса vector входным параметром был бы объект, созданный на основе класса matrix), то для доступа к за-крытым членам объекта-параметра необходимо было бы применять специальные средства. Это связано с тем, что единицей защиты явля-ется не объект, а тип, то есть методы объекта могут обращаться к за-крытым членам не только данного объекта, но и к закрытым членам любого объекта данного типа.

 

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

 

В отличие от конструктора преобразования, входной параметр конструктора копирования имеет тип, описываемый данным классом. Таким образом, если описывается класс Х, то его конструктор копирования может иметь один из сле-дующих прототипов:

 

Х(Х&);

 

Х(const Х&);

 

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

 

Пример:

 

box* b4 = new box(2,3,5); // Явный запуск конструктора

 

  // с тремя параметрами. Адрес  
  // динамически созданного  
  // объекта типа box  
  // присваивается переменной  
box b5 = * b4; // b4.  
// Разыменование указателя на объект,  
  // т.е. получение доступа к  
  // информации, хранящейся в нем, и  
  // использование ее для инициализации  
  // создаваемого объекта.  

box b6 = box(4,7,1); // Создание временного объекта и

 

// инициализация именованного

 

// объекта.

 



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 253; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.225.31.159 (0.141 с.)