Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Листинг 10.7






 

#include < iostream.h>

class Test

{

public:

Test (int = 0);

void print() const;

private:

int x;

};

 

Test:: Test(int a) {x = a; }//конструктор

 

void Test:: print() const

{

cout < < ”x = “< < x < < endl < < ”this-> x = “ < < this -> x< < endl < < ”(*this).x=”< < (*this).x< < endl;

}

main()

{

Test a(12);

a.print();

return 0;

}

Перегрузка операций. Любая операция, определенная в C++, может быть перегружена для создан­ного класса. Это делается с помощью функций специального вида, называемых функциями-операциями (операторными функциями). Общий вид такой функции:

 

возвращаемый_тип operator # (список параметров)

{

тело функции

}

 

где вместо знака # ставится знак перегруаемой операции.

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

Например, два варианта перегрузки операции сложения для класса Point: первый вариант - в форме метода класса:

 

class Point

{

double x. у: public:

//...

Point operator +(Point&);

};

Point Point:: operator +(Point& p)

{

return Point(x + p.x, у + р.у);

}

 

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

 

class Point

{

double x, у;

public:

//...

friend Point operator +(Point&. Point&);

};

Point operator +(Point& p1. Points p2)

{

return Point(p1.x + p2.x. p1.у + p2.y);

}

 

Независимо от формы реализации операции «+» можно теперь написать:

 

Point pl(0, 2), р2(-1, 5);

Point рЗ = p1 + р2;

 

Встретив выражение pi + р2, компилятор в случае первой формы перегрузки вызовет метод p1.operator + (p2), а в случае второй формы перегрузки - глобальную функцию operator + (p1, р2).

Результатом выполнения данных операторов будет точка рЗ с координатами х = -1, у = 7.

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

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

Чтобы компилятор смог различить эти две формы операции инкремента (декремента), для них используются разные сигнатуры, например:

 

Point& operator ++(); // префиксный инкремент

Point operator ++(int); // постфиксный инкремент

 

Реализация данных операций на примере класса Point:

 

Point& Point:: operator ++()

{

x++;

y++;

return *this;

}

Point Point:: operator ++(int)

{

Point old = *this;

X++;

y++;

return old;

}

 

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

Использование префиксного инкремента (декремента) для параметра цикла for дает бо­лее эффективный программный код.

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

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

Например, для класса Man перегрузку операции присваивания мож­но определить следующим образом:

 

// Man.h (интерфейс класса) class Man

{

public:

//...

Man& operator =(const Man&): //операция присваивания private:

char* pName;

II...

};

// Маn.срр (реализация класса) //...

Man& Man:: operator =(const Man& man)

{

if (this == & man) return *this; // проверка на самоприсваивание

delete [] pName; //уничтожить предыдущее значение

pName = new char[strlen(man.pName) + 1];

strcpy(pName. man.pName);

birth_year = man.birth_year;

pay = man.pay;

return *this;

}

 

Моменты реализации операции присваивания:

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

- удалите предыдущие значения полей в динамически выделенной памяти;

- выделите память под новые значения полей;

- скопируйте в нее новые значения всех полей;

- возвратите значение объекта, на которое указывает this (то есть *this).

Статические элементы класса. До сих пор одноименные поля разных объектов одного и того же класса были уникальными. Но что делать, если необходимо создать переменную, значение которой будет общим для всех объектов конкретного класса? Если воспользо­ваться глобальной переменной, то это нарушит принцип инкапсуляции данных. Модификатор static как раз и позволяет объявить поле в классе, которое будет общим для всех экземпляров класса. Кроме объявления статического поля в классе, необходимо также дать его определение в глобальной области ви­димости программы, например:

 

class Coo

{

static int count: // объявление в классе // остальной код

};

int Coo:: count = 1; // определение и инициализация

// int Coo:: count; // по умолчанию инициализируется нулем

 

Аналогично статическим полям могут быть объявлены и статические методы класса (с модификатором static). Они могут обращаться непосредственно толь­ко к статическим полям и вызывать только другие статические методы класса, потому что им не передается скрытый указатель this. Статические методы не могут быть константными (const) и виртуальными (virtual). Обращение к статическим методам производится так же, как к статическим полям - либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.