
Лекция 6
Возвращение объектов функциями.
В языке С++ функции могут возвращать в качестве результатов объекты. Чтобы функция могла вернуть объект как результат, нужно объявить в качестве ее типа имя класса и обеспечить возврат объекта этого типа через оператор return.
Рассмотрим пример.
#include <iostream.h>
class MyClass
{int value;
public:
MyClass(int i)
{value=i;
cout<<”В конструкторе.\n”;}
~MyClass()
{cout<<”Разрушение объекта.\n”;}
int getvalue()
{return value;}
MyClass change()
{ MyClass object(value*2);
return object;}
/* функция change() возвращает объект, у которого поле value в 2 раза больше поля вызывающего объекта. */
};
//Внешняя функция
void display(MyClass object)
{cout<<object.getvalue()<<’\n’;}
void main()
{MyClass a(10);//создание объекта а и инициализация поля value значением
10
display(a);
a=a.change();/*создание нового объекта и присваивание его значения объекту а */
display(a);
cout<<”После выхода из функции display().\n”;}
Результат
В конструкторе.
10
Разрушение объекта.
В конструкторе.
Разрушение объекта.
Разрушение объекта.
20
Разрушение объекта.
После выхода из функции display()/
Разрушение объекта.
В этом примере функция change() создает локальный объект object класса MyClass, в котором его элемент value содержит вдвое большее значение, чем соответствующий элемент вызывающего объекта. Этот объект возвращается функцией change() и присваивается объекту а функции main(). Затем объект object разрушается , о чем свидетельствует первое сообщение “Разрушение объекта”.
Если функция возвращает объект как результат, то автоматически создается временный объект, который хранит результат. После выхода из функции значение временного объекта разрушается. В связи с этим появилось второе сообщение о разрушении объекта.
Разрушение временного объекта может вызывать побочные эффекты. Например, если объект, возвращаемый функцией, выделяет ДП и имеет деструктор, который освобождает ее, то этот участок ДП будет освобожден даже в том случае, если объект, получающий возвращаемое функцией значение, все еще ее использует. Поэтому в программу в этом случае нужно включать копирующий конструктор.
Кроме того, побочные эффекты могут возникать при инициализации одного объекта другим. Например, допустим, что класс MyClass выделяет ДП для каждого объекта., и объект А является его экземпляром. Предположим, что объект А инициализирует объект В:
MyClass B=A;
Объект В будет копией объекта А. Следовательно, объект В также будет ссылаться на область памяти, выделенную объектом А. Если класс содержит деструктор, то при уничтожении А и В одна и та же область памяти будет освобождаться дважды. Возникает ошибка.
В основе перечисленных проблем лежит создание копии объекта. Поэтому нужно создавать собственный конструктор копии.
Формат конструктора копии
< имя класса>{const <имя класса>& object)
{<тело конструктора>}
Здесь object- это ссылка на объект, который используется для инициализации другого объекта.
Рассмотрим пример программы, в которой необходим собственный конструктор копии при инициализации одного объекта другим.
#include <iostream>
class array
{int *p_int;//указатель на целое число
int size;//длина массива
public:
array(int n)
{p_int=new int[n];
size=n;
}
~array()
{delete []p_int;}
array(const array &a);//прототип конструктора копии
void put(int i, int value)
{if(i>=0 && i<size)
p_int[i]=value;}
/* эта функция записывает значение value в элемент массива p_int c индексом i */
int get(int i)
{return p_int[i];}
//возвращает элемент массива p_int по индексу
};
array::array(const array & a)
{int i;
p_int=new int[a.size];
size=a.size;
for(i=0; i<size;i++}
p_int[i]=a.p_int[i];
}
void main()
{array massiv(10);
int i;
for(i=0; i<10;i++)
massiv.put(i);
for(i=0; i<10;i++)
cout<<”\n”<<massiv.get(i);
array x=massiv:
//Вызывается конструктор копии, который в объект x записывает копию объекта massiv, но адреса p_int у них разные */
for(i=0; i<10;i++)
cout<<”\n”<<x.get(i);
}
При вызове конструктора копирования для нового массива выделяется память. Адрес этой памяти хранится в поле x.p_int. Таким образом, объекты x и massiv содержат одинаковые массивы. Однако эти массивы имеют разные адреса: massiv.p_int не равен x.p_int. Деструктор будет уничтожать копию объекта, в котором массив записан по адресу x.p_int , а исходный объект- по адресу massiv.p_int.
Если бы в программе не было конструктора копии, то указатели x.p_int и massiv.p_int содержали бы один и тот же адрес, и тогда деструктор бы дважды разрушал одну и ту же область памяти. Это ошибка.