Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву
Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Манипуляции с состоянием объектаСодержание книги Поиск на нашем сайте
Для доступа к внутренним информационным членам объекта, созданного на основе класса (чтение/запись), необходимо использовать специальные методы класса, называемые модификаторами (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)
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 b6 = box(4,7,1); // Создание временного объекта и
// инициализация именованного
// объекта.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Последнее изменение этой страницы: 2017-02-05; просмотров: 346; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.216.156 (0.013 с.) |