Читайте также:
|
|
C++ дает возможность использовать стандартные операции для классов, в дополнение к обычным операциям для фундаментальных типов. Например, для целых переменных справедливо:
int a, b, c;
a = b + c;
Тем не менее, это не очевидно для следующего случая (фактически, это – неверно!):
struct { char product [50]; float price; } a, b, c;
a = b + c;
Присваивание объектов одного и того же класса (или struct) друг другу разрешено (копирующий конструктор по умолчанию!). Ошибка здесь в применении операции сложения, которая не поддерживается для не базовых типов.
Но в C++ операции можно перегрузить(т.е. расширить область их действия в “пространстве” типов) и тогда эти операции можно применять к объектам, создаваемым на основе классов. Вот список всех операций, которые могут быть перегружены:
+ - * / = < > += -= *= /= << >><<= >>= ==!= <= >= ++ -- % & ^! |~ &= ^= |= && || %= [] () new deleteДля перегрузки операции нам нужно написать функцию-член класса, чье имя составлено из ключевого слова operator и, следующего за ним знака (имени) операции, в соответствии с таким синтаксисом (для прототипа):
type operator sign ( parameters );
Вот пример для операции + . Мы собираемся сложить двумерные векторы a (3,1) и b (1,2) . Здесь мы используем покоординатное сложение, так что результат будет (3+1,1+2) = (4,3) .
// vectors: overloading operators example #include <iostream.h> class CVector { public: int x,y; CVector () {}; CVector (int,int); CVector operator + (CVector);};CVector::CVector (int a, int b) { x = a; y = b;}CVector CVector::operator+ (CVector param) { CVector temp; temp.x = x + param.x; temp.y = y + param.y; return (temp);}int main () { CVector a (3,1); CVector b (1,2); CVector c; c = a + b; cout << c.x << "," << c.y; return 0;}Здесь имя класса присутствует несколько раз в разных контекстах:
CVector (int, int); // имя функции Cvector(конструктор) CVector operator+ (CVector); // функция operator+, которая //возвращает тип CVectorФункция operator + класса CVector – это та функция, которая ответственна за перегрузку арифметической операции + . Она может быть вызвана любым из двух способов:
c = a + b;
c = a.operator+ (b);
àЗаметим, что мы включили в класс пустой конструктор (без параметров) и с пустым телом:
CVector () { };
Это необходимо, поскольку уже существует другой конструктор,
CVector (int, int);
Поэтому никакой конструктор по умолчанию не предполагается, и мы должны явно “его” определить, иначе объявление
CVector c;
включенное в main () не сработает.
Отметим, также, что no-op блок конструктора не рекомендуется использовать, поскольку тогда нет инициализации полей класса. В нашем случае, более подходящим было бы написать что-нибудь подобное:
CVector () { x=0; y=0; };
Но, для простоты, мы это опустили.ß
Если класс включает по умолчанию специальные конструкторы, то в него по умолчанию входит и определение оператора присваивания ( = ) для объектов одного и того же класса. При этом копируется все содержимое нестатических данных объекта-аргумента (справа) в объект слева. Можно, конечно, переопределить функциональность этого оператора, если мы хотим копировать только часть полей класса.
При перегрузке операций рекомендуется не менять математический смысл операций.
Хотя прототип функции operator + выглядит достаточно очевидно, не для всех операций это так. Ниже мы приводим сводную таблицу, показывающую, как должны объявляться различные operator-функции (заменяем @ на знак операции в конкретном случае):
Выражение | Операция (@) | Член-функция | Глобальная функция |
@a | + - * &! ~ ++ -- | A::operator@() | operator@(A) |
a@ | ++ -- | A::operator@(int) | operator@(A, int) |
a@b | + - * / % ^ & | < > ==!= <= >= << >> && ||, | A::operator@(B) | operator@(A, B) |
a@b | = += -= *= /= %= ^= &= |= <<= >>= [ ] | A::operator@(B) | - |
a(b, c...) | () | A::operator()(B, C...) | - |
a->b | -> | A::operator->() | - |
* здесь a – объект класса A , b – объект класса B и c – объект класса C .
В этой таблице мы можем видеть, что для некоторых операций имеются два способа перегрузки операций над классами: как метод класса и как глобальной функции. Их использование почти неразличимо, однако не будет лишним напомнить, что не-члены класса не могут иметь доступ к закрытым и защищенным полям класса, если глобальная функция не будет сделана дружественной (friend) нашему классу.
Ключевое слово this
Ключевое слово this представляет внутри класса адрес объекта класса в памяти, во время его существования. Это указатель, чье значение всегда – адрес конкретного, существующего, объекта.
Он может быть использован для проверки, является ли параметр, передаваемый в функцию-член некоторого объекта, самим этим объектом. Например,
// this #include <iostream.h> class CDummy { public: int isitme (CDummy& param);}; int CDummy::isitme (CDummy& param){ if (¶m == this) return 1; else return 0;} int main () { CDummy a; CDummy* b = &a; if (b->isitme(a)) cout << "yes, &a is b"; return 0;}yes, &a is b
Указатель this часто используется в функциях-членах operator = , которые возвращают объекты по ссылке (при этом мы избегаем использования временного объекта). Для приведенного выше примера с вектором, мы могли бы написать функцию operator = таким образом:
CVector& CVector::operator= (const CVector& param){ x=param.x; y=param.y; return *this;}В действительности, это вероятный код, который сгенерируется для нашего класса, если мы не вставим в класс никакой функции operator = .
Когда некоторый метод класса вызывается для конкретного объекта класса, то указатель на этот объект передается методу. Этот указатель, называемый this, указывает на этот конкретный объект. Указатель this есть константный указатель и не может быть изменен (переадресован). Если функция-член является константной функцией, то для нее указатель this есть константный указатель на константу. Если мы хотим модифицировать наш объект внутри функции и вернуть модифицированный объект вызывающей “программе”, мы можем вернуть *this. Вот пример, более сложный, показывающий явное использование указателя this:
// В этом примере определяется класс Item, и функции для модификации объекта этого класса. Используя указатель this может быть выполнена модификация объекта с помощью сцепления функций.
#include <iostream.h>
class Item
{
double price;
int quantity;
public:
Item(double p, int q): price(p), quantity(q) { }
Item& modify_price(double new_price)
{
price = new_price;
return *this;
}
Item& modify_quantity(int new_quantity)
{
quantity = new_quantity;
return *this;
}
void print_item() const
{
cout << "price = " << price << endl
<< "quantity = " << quantity << endl;
}
};
int main()
{
Item a(0.29, 100);
a.print_item();
a.modify_price(0.39).modify_quantity(20);
a.print_item();
return 0;
}
Программа выводит:
price = 0.29
quantity = 100
price = 0.39
quantity = 20
Заметим, что функции modify_price() или modify_quantity() свою основную работу сделают и в том случае, если они не будут возвращать *this. Но тогда нельзя будет использовать сцепление функций. К тому же, эти функции должны возвращать ссылки. В противном случае, последующая функция вызывается из копии объекта *this, который не является исходным объектом. Например, если бы возврат из функции modify_price был по значению, тогда бы вывод был
price = 0.29
quantity = 100
price = 0.39
quantity = 100
Дата добавления: 2015-11-16; просмотров: 46 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Указатели на классы | | | Статические члены |