|
Программирование >> Унарные и бинарные операторы
Как видим, сначала освобождается память, занимаемая объектом, затем оператор узнает размеры копируемого массива, выделяет столько памяти, чтобы 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ШЫми приоритет (оператор умножения * всегда выполняется раньше, чем оператор сложения +) и порядок вьнюл-иения (оператор + выполняется слева направо, независимо от того, какие объекты складываются ) операторов. В отличие от собственных функций, допустимое число параметров различных операторных функций определяется природой реализуемых ими операторов.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |