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

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


Доступ к массиву

у двухмерного массива matrix, который мы начали строить в предыдущей главе, есть пока множество недостатков. Один из самых .заметных - неуклюжие собственные функции putC) и get(), предназначенные для работы с отдельными элементами массива. Вместо записи m=a.get(2.2) хотелось бы видеть т=а[2][2] или т=а(2.2), авместо a.put(2.2.10.0) - более привычное а[2][2]=10.0 илпа(2.2)=10.0.

Как вы уже догадались, это возможно за счет специальных собственных функций объекта, называемых операторными. Так эти функции называются не только потому, что они содержат слово operator в своем имени, по и потюму, что позволяют придать новый смысл обычному оператору, такому как +, = или . Ведь сложение, присваивание или вывод на экран отличаются для разных типов объектов.

Попробуем создать оператор О для класса matri х. Соответствующая операторная функция принимает два параметра - номер строки и номер столбца - и возвращает значение типа double:

class matrix{

double operator()(int r.int c){ return ra[r*ncols+c]:

Поскольку операторная функция - это собственная функция объекта (ее название operatorO), вывести на экран значение элемента массива можно, записав:

cout a.operator()(2.2) endl:

Напомню, что операторные функции (как и любые собственные 1)ункции) можно определять вне класса, по в этом случае их имена П1)едваряются именем класса (в нашем случае - matrix) и оператором расширения области видимости (::),

Но ДЛЯ собственных функций, начинающихся словом operator , язык С++ предусматривает удобную сокращенную запись, позволяющую убрать точку и само это слово. В нашем случае получится .запись, естественная для двухмерного массива:

cout а(2.2) endl:

К сожалению, определение операторной функции для оператора О, данное чуть вьппс, не позволяет использовать его для изменения значений .элементов массива. Инструкция а(2.2)=10.0 вызовет сооб1цение компилятора об оинбке, гго-то вроде -gnon-lvalue in assignment* (присваиваем не левому значению). Все дело в том, что заданная нами функция возвращает зшченг/ нерс-мснной типа double, а не саму нерсменпую. А конкретное значение не может стоять слева от оператора присваивания. Разве можно записать 2=10.0?

Возвращаемое нашей опсратор!юп функцией значение - типично правое (rvalue), оно может стоять только справа от оператора присваивания. Чтобы стать левым , оно должно превратиться из значения .элемента массива в сам этот элемент или ссылку на него. Поэтому нужно, чтобы операторная функция возвращала ссылку, а не значенпе. Новое определение операторной функции должно выглядеть так:

matrix: :double & operatorO (int r.int c){ return m[r*ncols+c]:

Оператор & командует операторной функции вернуть ссылку на элемент массива m[r*ncol s+c], а ссылка - это синоним или другое имя переменной. Значит, с ней



matrixdnt r=2.int с=2){ конструктор

nrows=r:

ncols=c:

m=new double[r*c]: }

-matrix(){ деструктор

delete [] m;

matrixCraatrix &mc){ конструктор копий nrowsnc.rgetO: ncols=mc.cget(): m=new double[nrows*ncols]; forCint i=0:i<nrows;i++) for(int j-0:j<ncois;j++) m[i*ncols+j]=mc(i.j): оператор ()

double & operatorOCint r.int c){ return m[r*ncols+c];

int rget(){return nrows:}

int cgetojreturn ncols:}

private:

int nrows:

int ncols;

double *П);

Обратите внимание, насколько проще стал конструктор копии. Инструкцию putd.j.mc.getCi.j)) теперь заменяет гораздо более понятная запись:

m[i*ncols+j]=mc(i.j):

Равенство

Работа над классом matrix, начатая в прошлой главе, сопровождалась цепью катастроф. Создав конструктор, мы получили утечку памяти, устраненную деструктором. Но с появлением деструктора объект уничтожался при передаче его функции. Бороться с этим прихо-

МОЖНО делать что угодно: показывать на экране, менять, запоминать в другой переменной. Программа, показанная в листинге 9.1, демонстрирует возможности оператора (), приспособленного для доступа к элементу двухмерного массива matrix.

Листинг 9.1

#include <iostream> using namespace std: finclude matrix2.h #define NOFCOLS 100 #define NOFROWS 200 int main(){ int i.j:

matrix a(NOFROWS.NOFCaS):

a(2.2)=10:

double &m=a(2.2);

cout ra endl: 10

m-5:

cout a(2.2) endl:

return 0:

В файле matrix2.h хранится определение класса matrix с оператором (), возвращающим ссылку на элемент массива. В тексте программы элемент (2,2) сначала становится равным десяти, затем создается ссылка m на элемент массива (2,2). Далее на экран выводится число 10 - значение этой ссылки. Наконец, инструкция т=5 записывает в элемент массива (2,2) число 5, которое и выводится на экран в последней строке:

cout а(2.2) endl;

Созданный нами оператор доступа к элементу двухмерного массива делает ненужными функции getO н put О, теперь их можно убрать из определения класса matrix. Получивщийся класс показан в листинге 9.2.

Листинг 9.2

class matriх{ public:



Ho DTO ие одно и то же: конструктор копии используется при создании обтлкта, а оператор присваивания приравнивает существующий объект другому.

дилось путем создания специального конструктора копии, создающего в другой области памяти дубликат объекта.

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

Листинг 9.3

#inclucle <iostream> using namespace std: #include matrix2.h #define NOFCOLS 100 #define NOFROWS 200 void foo(matrix &m){

matrix tnp:

tnp=m:

cout tmp(2.10) endl:

int main(){ int i.j:

matrix a(NOFROWS.NOFCOLS): for(i=0:i<NOFROWS;i++) for(j=0:J<NOFCOLS:j++) a(i.j)=i*j: f00(a):

cout a(2.10) endl: return 0:

Внутри функции foo() объявляется объект tmp типа matrix. Поскольку число строк и столбцов при этом не указывается, конструктор создает массив размером 2x2. Затем объекту tmp приравнивается массив а (функции передается ссылка на а). И хотя размеры обоих массивов не совпадают (tmp имеет размеры 2 х 2, в то время как а - 2000 х 1000), ничего страшного не происходит, потому что оператор присваивания по умолчанию копирует объекты бит за битом. После присва-

ивания переменная nrows объекта tmp будет равна 200, переменная ncols - 100, а m будет указывать на область памяти, где хранятся переменные массива а. То есть tnp становится после присваивания точной копией массива а. Вот в этом как раз и таится стран! пая опасность, потому что деструктор уничтожит объект tmp перед выходом из функции, а вместе с ним освободит и память, на которую указывает т. Но память, используемая массивами tmp и а, - общая; погибая, объект tmp тянет за собой и своего сиамского брата-близнеца, и теперь попытки использования массива а в основной программе приведут, скорее всего, к ее аварийному завершению.

Чтобы устранить этот недостаток, нужно, очевидно, так определить оператор присваивания, чтобы для копии объекта выделялась другая область памяти. Тогда деструктор упи ггожит копию, по пе тронет основной объект, передаваемый функции.

Оператор присваивания имеет много общего с конструктором копии, и первый вариант операторной функции для него может выглядеть так, как показано в листинге 9.4.

Листинг 9.4

void matrix: :operator=(matrix &mc){ delete [] m: nrows=nic.rget(): ncols=nc.cget(): 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):



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

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