![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Листинг 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; }
Моменты реализации операции присваивания: - убедитесь, что не выполняется присваивание вида х = х. Если левая и правая части ссылаются на один и тот же объект, то делать ничего не надо. Если не перехватить этот особый случай, то следующий шаг уничтожит значение, на - удалите предыдущие значения полей в динамически выделенной памяти; - выделите память под новые значения полей; - скопируйте в нее новые значения всех полей; - возвратите значение объекта, на которое указывает this (то есть *this). Статические элементы класса. До сих пор одноименные поля разных объектов одного и того же класса были уникальными. Но что делать, если необходимо создать переменную, значение которой будет общим для всех объектов конкретного класса? Если воспользоваться глобальной переменной, то это нарушит принцип инкапсуляции данных. Модификатор static как раз и позволяет объявить поле в классе, которое будет общим для всех экземпляров класса. Кроме объявления статического поля в классе, необходимо также дать его определение в глобальной области видимости программы, например:
class Coo { static int count: // объявление в классе // остальной код }; int Coo:: count = 1; // определение и инициализация // int Coo:: count; // по умолчанию инициализируется нулем
Аналогично статическим полям могут быть объявлены и статические методы класса (с модификатором static). Они могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, потому что им не передается скрытый указатель this. Статические методы не могут быть константными (const) и виртуальными (virtual). Обращение к статическим методам производится так же, как к статическим полям - либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.
|