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

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


(копия аргумента) создан, перед выходом из функции должен вызываться деструктор, который мы уже написали - себе на погибель. Ведь в деструкторе есть инструкция delete [] ш, значит, он освободит память, занимаемую нашим объектом, ведь именно на нее указывает ш! То есть при выходе из функции двухмерный массив перестанет существовать, и попытки записать что-то в область памяти, которой программа уже не обладает, приводят к ее аварийному завершению.

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

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

Итак, конструктор копии для класса matrix может

выглядеть так:

matrixCmatrix &mc){

nrows.rgetO:

ncol s.cgetO:

m=new double[nrows*ncols]:

forCint i-0:i<nrows:i++)

forCint j=0;j<ncols:j++) putd.j.mc.getCi.j)):

В этом конструкторе сначала выясняются размеры копируемого объекта, затем оператор new выделяет новую область памяти, куда и переписываются элементы исходного двухмерного массива тс.

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

Но во всем полагаться на компилятор нельзя, потому что не всегда можно правильно понять его jrarHKy. Например, следующая инструкция создает указатель р на объект типа matriх:

matrix *p-new matriхС100.200):

При этом вызывается конструктор класса matrix, выделяющий память для двухмерного массива. Но автоматического вызова деструктора в этом случае не происходит, потому что компилятор считает, что создает всего лишь указатель, а не двухмерный массив. Значит, при выходе из функции или там, где объект, созданный оператором new, должен исчезнуть, необходимо записать инструкцию:

delete р:

Тогда память, занимаемая двухмерным массивом, освободится.

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



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

matrix b=a:

Примеры использования конструктора копии демонстрирует программа, пока.занпая в листинге 8.9.

Листинг 8.9

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

matrix a(NOFROWS.NOFCOLS): constr a cout endl: for(i=0:i<NOFROWS:i++) for(j=0:j<NOFCOLS:j++)

a.put(i.j.i*j): matrix b=a; copy constr matrix c(b): copy constr cout c.get(2.1) endl: destr c.destr b.destr a return 0: }

В файле matrixl.h находится определение класса matrix с конструктором, деструктором и конструктором копии. Обычный конструктор вызывается в программе один раз - при со.здании объекта а. Далее вызывается конструктор копии при создании объекта b (matrix b=a), а также при создании объекта с (matrix c(b)).

I lauia программа создает три объекта, которые зан н -мают разные области памяти, по содержат одинаковые двухмерные массивы. После вывода на экран .элемента (2,1) массива с программа должна уничтожить вес

объекты, причем объект, созданный последним, погибает первым: сначала вызывается деструктор массива с, затем b и, наконец, а.

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

matrix(int r.int с){

nrows=r:

ncols-c:

m=new double[r*c]:

cout constr endl:

Пусть деструктор выводит строку destr , а конструктор копии - строку сору . Тогда, следя затем, что программа показывает на экране, можно понять, когда и что вызывается. Для большей ясности создадим дополнительные инструкции вывода и в основной программе:

cout 1 endl: matrix b=a: copy constr cout 2 endl; matrix c(b); copy constr cout 3 endl:

Программисты часто ставят подобные эксперименты, чтобы лучше понять, как работает компилятор. Конечно, существует стандарт С++, где поведение компилятора точно описано, по собственные эксперименты Гораздо быстрее учат языку, чем самые толстые руководства. Надо, правда, знать, что стандарт некоторые вен1и оставляет на усмотрение компилятора. Нельзя,



например, выяснять, каким будет результат инструкции i=i++, потому что другой компилятор может выдать все что угодно (см. об этом раздел Унарные и бинарные операторы в главе 3), здесь эксперименты бесполезны.

ГЛАВА 9 Операторные функции




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

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