Программирование >>  Унарные и бинарные операторы 

1 ... 20 21 22 [ 23 ] 24 25 26 ... 37


Как видим, сначала освобождается память, занимаемая объектом, затем оператор узнает размеры копируемого массива, выделяет столько памяти, чтобы on смог там поместиться, и копирует элемехггы массива - строка за строкой.

Задача 9.1. Всегда ли оператор присваивания должен уничтожать память, занимаемую двухмерным массивом?

Указатель This

- Ладно. Пусть лучший вариант будет. Представь, что он из своей машины вылез, подошел к твоей, и только ты шмалять собрался, глядь... - Володин выдержал значительную паузу. - Глядь, а это Не он, а ты сам и есть. А тебе шма лять надо. Скажи, поедет от такого крыша?

Виктор Пелевин, Чапаев и Пустотар

Казалось бы, оператор присваивания, показанный в листинге 9.4, делает все, что от него требуется. Но давайте-представим, что в программе объект а копируется сам в себя (а-а;). Хоть смысла в этом немного, но такая операция разрешена, а значит, должна вьшолняться. Но рассматриваемый оператор присваивания сначала освобождает намять объекта, а потом выделяет для него другую ее область. Это значит, что в случае присваивания а=а копирование элементов массива а будет идти из уже освобожденной области памяти. А язык С++ устроен так, что освобожденная память может в любой момент быть занята чем-то другим. Получается, что копирование идет из того места, где объекта уже нет: присваивание оборачивается уничтожением.

Ясно, что избежать уничтожения при самоприсваивании можно, лишь имея возможность отличать себя

ОТ других. Для этого каждый объект располагает специальным указателем на себя this (в переводе с английского это ). Указатель this не объявляется, но все происходит так, как будто он незримо присутствует в классе. Например, в классе matrix наряду с размерами массива и указателем на область памяти как бы присутствует еще и указатель this:

class matrix{ public:

private:

matrix *this: невидимый указатель на объект

int nrows: int ncols: double *m:

Этот указатель thi s, в отличие от nrows и ncol s, возникает автоматически при созданий объекта и хранит его адрес. Поэтому thi s можно использовать, чтобы отличить свой объект от чужого в новом варианте оператора присваивания (листинг 9.5).

Листинг 9.5

void operator=(matrix &mc){ if(this != &iic){ delete [] m: nrows=mc.rget(); ncols=mc.cget(); m=new double [nrows*ncols]: for(int i=0:i<nrows:1++) for(int J=0:j<ncols:j++) m[i*ncols+j]=mc(i.j):

Смысл условия if (this !- &mc) прост: если указатель на текущий объект this равен адресу копируемого объекта, то .это ты сам и есть , и тогда BOo6nie ничего



Условие (this != &nic) может быть неправильно понято. В нем this - укамтель па объект, & тс - адрес объекта, поскольку & - это оператор получения адреса, а вовсе не ссылка.

delete [] m:

nrows=mc.rget(): ncols-mc.cgetO: m=new double [nrows*ncols]: for(int i=0:i<nrows:i++)

for(int j=0:j<ncols:J++) m[i*ncols+j]=mc(i.j):

return *this: 1

Посмотрим, как выполняется с новым оператором цепочка операций присваивания с=Ь=а. Сначала вызывается оператор присваивания объекта Ь, то есть this указывает в этом случае имешю па Ь. После присваи ва-ния оператор возвращает ссылку на b (return *this) оператору присваивания с. Оператор, как и положено, принимает ссылку, копирует данные из объекта b в объект с и снова возврап1ает ссылку - на этот раз на объект с и, поскольку дальнейших операций присвапвапия нет. эта ссылка исчезает в пустоте.

Раз уж речь зашла об указателе this, стоит сказать несколько слов о внутреннем устройстве объектов. Оказывается, собственные функции и данные хранятся в памяти компьютера отдельно. Объектов, принадлежащих одному классу, может быть много, а набор собственных функций у них один. Собственной функции передается при вызове указатель this, с помоп1ью которого она получает доступ к данным именно это<{о объекта. Даже функции, не имеюп1сй явных параметров, тайно передается указатель this и, например.

Заметим, что возврат ссылок, так же как их передача, выглядит в языке С++ довольно странно. Ведь return (*this) означает возврат объекта, а не ссылки па него (this - указатель, а оператор * превращает указатель в са.м объект). Но поскольку указано, что функция возвращает ссылку, то объект испытывает cnie одно невидимое прсвращепис.

делать не надо - остается только покинуть операторную функцию оператора присваивания*.

Указатель this в операторной функции из листинга 9.5 позволяет избежать самоприсваивания. Ыо и ему не дано сделать оператор = идеальным.

Представим себе, что в программе появилась цепочка операций присваивания:

matrix a.b.c:

с = b = а;

С точки зрения языка С++ такая запись правильна и в соответствии со свойствами оператора = выполняется справа HiuicBo (см. приложение): сначала объект b становится равным а, а затем и с становится равным Ь. Если С++ оперирует стандартными объектами, такими как целые числа или строки (тип string), присваивание происходит правильно и в нужном порядке.

Но оператор присваивания, определенный в листинге 9.5, ожидает ссылку на объект, но сам ничего не воз-врап1ает. Поэтому цепочка операций присваивания, выполняемая с его помощью, прервется уже на первом шаге: при выполнении инструкции Ь=а операторная функция ничего не возвратит, а значит, объект с не сможет стать равным Ь. Совершенно ясно, что цепочка операций присваивания возможна, лишь когда оператор присваивания возвран1ает и прнгн1мает объекты одного типа. Если Hain оператор принимает ссылку на объект, то и возвран1ать он должен тоже ссылку, примерно так, как показано в листинге 9.6.

Листинг 9.6

matrix & operator=(matrix & mc){ if(this != &mc){



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

Например, операторная функция для оператора вызова функции О, который мы использовали в разделе Доступ к массиву , может, как и собственная функция, иметь сколько угодно параметров, а операторная функция для унарного (действующего на один объект) оператора вовсе не должна иметь параметров, потому что может найти объект по указателю thi s. А вот операторной функции для бинарного оператора достаточно передать один объект, потому что второй (тот, на который указывает this) всегда под рукой .

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

Чтобы проверить новый оператор, нам придется создать класс complex и реализовать в нем конструкторы и некоторые собственные функции, без которых не обойтись при выводе результатов на экран. Остов нового класса показан в листинге 9.7.

Листинг 9.7

class согф1ех{ public:

complexCdouble г. double i) : ге(г). im(i) {}

cofflplexCdouble г) : re(r). im(0) {}

complexO : re(0). im(0) {}

complex operator+CcOfflplex a){

complex tn?):

tnip.re=re + a.re;

tnip.im=im + a.im;

return tnp:

double getre(){return re;}

double getimO{return im;} продолжение

функция cget класса natrix (см. листинг 9.2) на самом деле устроена так:

int matrix::cget(matr1x *this){ return this->ncols:

Указатель this чаще всего остается незамеченным, но иногда, как, например, в операторе присваивания, его приходится использовать явно.

Друзья и родственники

Как бы мне не обменяться личностью: он войдет в меня, а я в него, - я охвачен полной безразличностью и боюсь решительно всего...

Саша Черный

До сих пор мы считали операторные функции языка С++ просто иначе названными собственными функциями. Но это не совсем так.

Операторные функции в С++, независимо от того, для какого класса определены, сохраняют нсизме1ШЫми приоритет (оператор умножения * всегда выполняется раньше, чем оператор сложения +) и порядок вьнюл-иения (оператор + выполняется слева направо, независимо от того, какие объекты складываются ) операторов. В отличие от собственных функций, допустимое число параметров различных операторных функций определяется природой реализуемых ими операторов.



1 ... 20 21 22 [ 23 ] 24 25 26 ... 37

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика