Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 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; просмотров: 253; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.225.31.159 (0.141 с.) |