Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Перегрузка операции присваивания




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

const monstr& operator = (const monstr &M){

/ / Проверка на самоприсваивание:

if (&М == this) return n h i s:

if (name) delete [ ] name;

if (M.name){

name = new char [strlen(M.name) + 1];

strcpyCname. M.name);}

else name = 0;

health = M.health; ammo = M.ammo; skin = M.skin;

return *this;

}

Возврат из функции указателя на объект делает возможной цепочку операций присваивания:

monstr А(10). В. С;

С = В = А;

Операцию присваивания можно определять только как метод класса. Она не наследуется.

Конструирование объекта в динамической памяти. При динамическом распределении памяти объекты размещаются в т.н. «куче» (англ. heap): при конструировании объекта указывается размер запрашиваемой под объект памяти, и, в случае успеха, выделенная область памяти, условно говоря, «изымается» из «кучи», становясь недоступной при последующих операциях выделения памяти. Противоположная по смыслу операция — освобождение занятой ранее под какой-либо объект памяти: освобождаемая память, также условно говоря, возвращается в «кучу» и становится доступной при дальнейших операциях выделения памяти.

Статические члены класса.

С помощью модификатора static можно описать статические поля и методы класса. Их можно рассматривать как глобальные переменные или функции, доступные только в пределах области класса.

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

Ниже перечислены особенности статических полей.

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

class А{

public:

static int count: // Объявление в классе

}:

int A::count: // Определение в глобальной области

// По умолчанию инициализируется нулем

// int A::count = 10: Пример инициализации произвольным значением

а Статические поля доступны как через имя класса, так и через имя объекта:

А *а. Ь:

cout «A::count «a->count «b.count:

// Будет выведено одно и то же

• На статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя изменить с помощью операции доступа к области действия, как описано выше. Это можно сделать только с помощью статических методов (см. далее).

• Память, занимаемая статическим полем, не учитывается при определении размера объекта с помощью операции sizeof.

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

class А{

static int count: // Поле count - скрытое

public:

static void inc_count(){ count++: }

}:

A::1nt count: // Определение в глобальной области

void f(){

А а:

// a.count++ - нельзя, поле count скрытое

// Изменение поля с помощью статического метода:

a.inc_count(): // или А::inc__count():

}

Статические методы не могут быть константными (const) и виртуальными (virtual).

Использование указателей на объекты.

К элементам классов можно обращаться с помощью указателей. Для этого определены операции.* и ->*. Указатели на поля и методы класса определяются по-разному.

Формат указателя на метод класса:

Возвр_тип (имя_класса::*имя_указателя)(параметры);

Например, описание указателя на методы класса monstr

int get_health() {return health;}

int get_ammoO {return ammo:}

(a также на другие методы этого класса с такой же сигнатурой) будет иметь вид:

int (monstr:: *pget)():

Такой указатель можно задавать в качестве параметра функции. Это дает возможность передавать в функцию имя метода:

void fun(int (monstr:: *pget)()){

(*this.*pget)(): // Вызов функции через операцию.*

(this->*pget)(): // Вызов функции через операцию ->*

}

Можно настроить указатель на конкретный метод с помощью операции взятия адреса:

// Присваивание значения указателю:

pget «& monstr::get_health;

monstr Vasia. *p;

p = new monstr;

// Вызов функции через операцию.*:

int Vasin_health - (Vasia.*pget)();

// Вызов функции через операцию ->*:

int p_heaUh = (p->*pget)();

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

• Указателю на метод можно присваивать только адреса методов, имеющих соответствующий

заголовок.

• Нельзя определить указатель на статический метод класса.

• Нельзя преобразовать указатель па метод в указатель на обычную функцию, не являющуюся элементом класса.

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

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

ПРИМЕЧАНИЕ

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

Формат указателя на поле класса:

тип_данных(иня_класса::*имя_указателя):

В определение указателя можно включить его инициализацию в форме:

&имя_класса::имя__поля;// Поле должно быть public

Если бы поле health было объявлено как public, определение указателя на него

имело бы вид:

int (monstr::*phealth)» &nranstr::health:

cout «Vasia.*phealth: // Обращение через операцию.*

cout «p->*phealth: // Обращение через операцию ->*

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

Наследование классов.

Наследование – отношение между классами, при котором один класс повторяет структуру и поведение другого класса (одиночное наследование) или других (множественное наследование) классов.

Класс, поведение и структура которого наследуется, называется базовым (родительским) классом, а класс, который наследует – производным классом. В производном классе структура и поведение базового класса (информационные члены и методы), дополняются и переопределяются. В производном классе указываются только дополнительные и переопределяемые члены класса. Производный класс является уточнением базового класса:

class z: public y{... };

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

Прототип объявления типа-наследника:

[class struct ]имя типа: [public protected private] имя базового типа {описание членов класса}

Пример:

struct A {

int x,y;

};

struct B: A {

int z;

};

A a1;

B b1;

b1.x = 1;

b1.y = 2;

b1.z = 3;

a1 = b1;

Объект типа B наследует свойства объекта типа A. Таким образом, объект типа-наследника содержит внутри себя члены базового типа:

При наследовании наследуются не только информационные члены, но и методы.Не наследуются:

􀂃 Конструкторы

􀂃 Деструктор

􀂃 Операция присваивания

Как уже было описано, единственный способ использования конструктора базового класса – список инициализации. Он записывается при описании конструктора производного класса:

При создании объекта производного типа B сначала будет вызван конструктор базового типа A. При этом если конструктору базового типа нужны параметры, то его необходимо вызывать явно в списке инициализации. Затем будет вызван конструктор производного типа B. Деструкторы вызываются в обратном порядке. При разрушении объекта производного типа сначала будет вызван деструктор этого типа, а затем деструктор базового типа. Допустимо присвоение объекту базового типа объекта производного типа. При этом объекту базового типа будет присвоена та часть объекта производного типа, которая структурно совпадает с базовым типом.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...