Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
369164_46F07_otvety_na_bilety_po_oop_si.doc
Скачиваний:
44
Добавлен:
24.12.2018
Размер:
613.38 Кб
Скачать

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

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

monster& operator = (const monster &M)

{

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

if (name) delete [] name;

if (M.name)

{

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

strcpy(name, M.name);

}

else name = 0;

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

return *this;

}

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

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

Можно определить функции-операции, которые будут осуществлять преобразование класса к другому типу. Формат:

operator имя_нового_типа ();

Тип возвращаемого значения и параметры указывать не требуется. Можно определять виртуальные функции преобразования типа.

Пример:

monster::operator int(){return health;}

...

monster Vasia; cout << int(Vasia);

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

void *operator new(size_t size);

void operator delete (void *);

где void * - указатель на область памяти, выделяемую под объект, size - размер объекта в байтах, size_t - тип размерности области памяти, int или long.

Переопределение этих операций позволяет написать собственное распределение памяти для объектов класса.

Операции, не допускающие перегрузки. В С++ существует несколько операций, не допускающих перегрузки:

. прямой выбор члена объекта класса;

.* обращение к члену через указатель на него;

?: условная операция;

:: операция указания области видимости;

sizeof операция вычисления размера в байтах;

# препроцессорная операция.

14. Дружественные функции

Иногда желательно дать функциям не членам возможность доступа к закрытой части класса. У класса есть механизм предоставления права доступа к своей закрытой части функциям не членам. Просто в описание класса помещается описание функции, перед которым стоит ключевое слово friend. Например, если имеется

class Vec; // Vec - имя класса

class vector {

friend Vec operator+(Vec, Vec);

//...

};

То вы можете написать

Vec operator+(Vec a, Vec b)

{

int s = a.size();

if (s != b.size()) error("плохой размер вектора для +");

Vec& sum = *new Vec(s);

int* sp = sum.v;

int* ap = a.v;

int* bp = b.v;

while (s--) *sp++ = *ap++ + *bp++;

return sum;

}

Одним из особенно полезных аспектов механизма friend является то, что функция может быть другом двух и более классов. Чтобы увидеть это, рассмотрим определение vector и matrix, а затем определение функции умножения.

Теперь, наконец, можно обсудить, в каких случаях для доступа к закрытой части определяемого пользователем типа использовать члены, а в каких - друзей. Некоторые операции должны быть членами: конструкторы, деструкторы и виртуальные функции, но обычно это зависит от выбора. Рассмотрим простой класс X:

class X {

// ...

X(int);

int m();

friend int f(X&);

};

Внешне не видно никаких причин делать f(X&) другом дополнительно к члену X::m() (или наоборот), чтобы реализовать действия над классом X. Однако член X::m() можно вызывать только для "настоящего объекта", в то время как друг f() может вызываться для объекта, созданного с помощью неявного преобразования типа. Например:

void g()

{

1.m(); // ошибка

f(1); // f(x(1));

}

Поэтому операция, изменяющее состояние объекта, должно быть членом, а не другом. Для определяемых пользователем типов операции, требующие в случае фундаментальных типов операнд lvalue (=, *=, ++ и т.д.), наиболее естественно определяются как члены. И наоборот, если нужно иметь неявное преобразование для всех операндов операции, то реализующая ее функция должна быть другом, а не членом. Это часто имеет место для функций, которые реализуют операции, не требующие при применении к фундаментальным типам lvalue в качестве операндов (+, -, || и т.д.).Если никакие преобразования типа не определены, то оказывается, что нет никаких существенных оснований в пользу члена, если есть друг, который получает ссылочный параметр, и наоборот. В некоторых случаях программист может предпочитать один синтаксис вызова другому. Например, оказывается, что большинство предпочитает для обращения матрицы m запись m.inv(). Конечно, если inv() действительно обращает матрицу m, а не просто возвращает новую матрицу, обратную m, ей следует быть другом.При прочих равных условиях выбирайте, чтобы функция была членом: никто не знает, вдруг когда-нибудь кто-то определит операцию преобразования. Невозможно предсказать, потребуют ли будущие изменения изменить статус объекта. Синтаксис вызова функции члена ясно указывает пользователю, что объект можно изменить; ссылочный параметр является далеко не столь очевидным. Кроме того, выражения в члене могут быть заметно короче выражений в друге. В функции друге надо использовать явный параметр, тогда как в члене можно использовать неявный this. Если только не применяется перегрузка, имена членов обычно короче имен друзей.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]