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

1 ... 17 18 19 [ 20 ] 21 22 23 ... 37


динамически), и возникает потребность описать не только его рождение, но и смерть.

Рассмотрим для примера массив с двумя индексами, где положение объекта определяется номером строки и номером столбца. В массиве, объявленном как int m[3] [2], - три строки и два столбца. Если учесть, что нумерация в C+-I- начинается с нуля, то элементы этого массива будут такими: ni[0][0], m[0][l], ni[l][0], m[l][l], п1[2][0], m[2][l]. Массивы с двумя индексами назывануг двухмерными, но поскольку память компьютера одномерна - это просто ряд идущих друг за другом байтов - массив хранится в ней построчно, сначала все элементы нулевой строки (у нас это т[0][0], т[0][1]), затем вес элементы первой строки и т. д.

Стандартные двухмерные массивы С++ идеально подходят для хранения изображений и матриц, но пользоваться ими неудобно, потому что нельзя задавать pa:j-меры массива в процессе выполнения программы; объявление int п1[3][2] создает массив с тремя строками и двумя столбцами - не болыпе и не менынс. Кроме того, со стандартными двухмерными массивами трудно работать функциям, ведь массивы с разным числом столбцов - это разные типы, вот и придется писать одну функцию для массивов с двумя столбцами, другую - для тех, что с тремя.

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

Если размеры массива заранее не известны, нужно научиться выделять память динамически, в С++ есть для этого специальный оператор new, с которым мы познакомились в разделе У казател и и массивы главы 5.

Следуюп1ие инструкции создают сначала указатель на double, азатем засылают в него адрес начала последовательности двадцати идуншх подряд переменных типа double:

double *m;

m = new double [20]:

A теперь вернемся к двухмерному массиву, создаваемому работающей программой на лету . Нас не должно смущать, что у массива два измерения, просто выделим оператором new память для одномерного массива и разместим в нем нап1 двухмерный массив - строка за строкой. Проект класса matrix, содержащего описание двухмерного массива, показан в листинге 8.6.

Листинг 8.6

class matrix{ public:

matrixCint r=2.int c=2){ nrows=r: ncols=c: m=new double[r*c]:

double getCint r.int c){ return m[r*ncols+c]: }

void put (int r.int c. double d){

m[r*ncols+c]-d:

int rgetO{return nrows:}

int cgetО{return ncols:}

private:

int nrows:

int ncols:

double *m:

Конструктор класса matrix имеет два параметра - число строк г и число столбцов с. Значения (int г=2. int с=2), пoкa;aнныc в списке, нужны при создании массива



без явно указанных размеров (например, инструкцией matrix а:).

Указание параметров по умолчанию позволяет иметь один конструктор, создающий массив либо заданных размеров (если они указаны), либо размером 2x2.

Оператор new выделяет для массива память, вмеща-юн1ую г*с переменных double. Двухмерный массив хранится в памяти построчно. Поэтому элемент, хранящийся в г-той строке и с-том столбце, за1шмает i юзицию r*ncols+c в одномерном массиве*.

В листинге 8.7 показана программа, обкатывающая вновь созданный объект а класса matrix (его описание, знакомое нам по листингу 8.6, хранится в файле matrix.h).

Листинг 8.7

#include <iostream> using namespace std: #include matrix.h

#define NOFCOLS 100 число столбцов #define NOFROWS 200 число строк void foe(matrix m){

cout foe: m.get(10.9) endl: }

int raain(){ int i.j:

matri X a(NOFROWS.NOFCOLS): for(i-0;i<NOFROWS:i++) for(j=0:j<NOFCOLS:j++) a.put(i.j.i*j); foo(a):

cout a.get(NOFROWS-l.NOFroLS-l):

return 0:

В этой программе сначала создается объект а - двухмерный массив из 200 строк и 100 столбцов, затем каж-

Как обычно, нумерация еш элементов начинается с нуля.

дому элементу присваиваются начальные значения, равные произведению номера строки на номер столбца. Далее созданный массив передается функции fooO, выводящей на экран содержимое элемента (10,9). Наконец, после возврата из функции на экран выводится крайний элемент из последней строки и последнего столбца, равный 99 х 199, то есть 19 701.

Конструктор, описанный в классе matrix (см. листинг 8.6), вызывается в нащей программе всего один раз - когда создается массив а. Интересно разобраться, что происходит при передаче массива функции fooO. Нам уже известно, что функция получает в этом случае дубликат передаваемого объекта, создаваемый специальным конструктором копии. Но если он не описан в соответствующем классе, вызывается конструктор копии по умолчанию, который передаст функции дубликаты всех элементов объекта. В нашем случае передадутся копии числа строк nrows, числа столбцов ncols и копия *П) - указателя на область памяти, где хранятся элементы массива. Эти копии уничтожатся после выхода из функции, так что программа из листинга 8.7 должна работать нормально.

Однако представим себе, что объект типа matrix создан внутри функции fooO. Тогда при его объявлении вызывается конструктор, выделяю!ций с помоп1ью оператора new память для двухмерного массива. Но поскольку в нашем классе мы пока не создал и деструктор объекта, при выходе из foo() активизируется деструктор по умолчанию, который уничтожит указатель на память, занимаемую массивом, но саму память не освободит, и она будет потеряна. Произойдет так называемая утечка памяти. С каждым вызовом f оо() свободной памяти, доступной программе, будет все меньше, в результате она кончится, и программа аварийно завершится.



Как же спастись в этом случае? Очевидно, объекту нужен деструктор, освобождаюи1ий память, выделенную внутри функции fooC). Выглядит он как собственная функция с таким же именем, как у класса, по предваренным тильдой (4iiatrix()). Внутри деструктора всего одна инструкция освобождипгя памяти (delete [] m:), на которую указывает т. Все вместе это выглядит следующим образом:

class tratrixf public:

-niatrix(){ delete [] m; }

private:

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

Листинг 8.8

void fpo(){

classl а: конструктор по умолчанию а {

class2 b: конструктор по умолчанию b class3 С; конструктор по умолчанию с

вызывается деструктор с вызывается деструктор b

вызывается деструктор а }

Конструктор копии

Итак, задача создания и уничтожения объектов решена. Если конструктор и деструктор соответствуют друг другу, память, временно занятая объектом, будет возвращена программе деструктором. Но при этом появляется новая трудность: программа из листинга 8.7 при использовании определения класса с деструктором аварийно .завершается. Какую же недопустимую операцию она выполняет?

Чтобы понять природу катастрофы, нужно еще раз представить себе, как передаются аргументы функции. Внутри функции foo(matrixm) {} из листинга 8.7 никакие объекты не создаются, функция просто принимает в качестве аргумента объект типа matrix. Этот объект передается по значению (см. раздел Параллельные миры в главе 5), то есть функция получает не сам объект, а его дубликат, создаваемыii специальным конструктором копии. А поскольку в определении нашего класса такого конструктора нет, вызывается конструктор копии по умолчанию, который создает полный аналог нашего аргумента. В объекте, создаваемом конструктором копии, - две целочисленных неременных, nrows и ncols, а также указатель т, содержащий адрес памяти, где расположен двухмерный массив.

С копией объекта внутри функции fooO можно делать что угодно: изменять массив или выводить на экран значения отдельных элементов, но раз объект

Объекты, созданные последними, уничтожаются первыми. В нашем примере объекты создаются конструкторами по умолчанию (вспомните, почему), а деструкторы автоматически вызьшаются, когда истекает время жизни объектов.



1 ... 17 18 19 [ 20 ] 21 22 23 ... 37

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