Другими словами, в обоих объектах (в оригинале и его копии) член данных p будет указывать на одну и ту же динамически выделенную область памяти. По завершении функции display() объект ob разрушается, и его разрушение сопровождается вызовом деструктора. Деструктор освобождает область памяти, адресуемую указателем ob.р. Но ведь эта (уже освобожденная) область памяти — та же самая область, на которую все еще указывает член данных (исходного объекта) a.p! Налицо серьезная ошибка.

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

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

// Одно из решений проблемы передачи объектов.

#include

#include

using namespace std;

class myclass {

  int *p;

 public:

  myclass(int i);

  ~myclass();

  int getval() { return *p; }

};

myclass::myclass(int i)

{

 cout << "Выделение памяти, адресуемой указателем p.\n";

 р = new int;

 *p = i;

}

myclass::~myclass()

{

 cout <<"Освобождение памяти, адресуемой указателем p.\n";

 delete p;

}

/* Эта функция HE создает проблем. Поскольку объект ob теперь передается по ссылке, копия аргумента не создается, а следовательно, объект не выходит из области видимости по завершении функции display().

*/

void display(myclass &ob)

{

 cout << ob.getval() << '\n';

}

int main()

{

 myclass a(10);

 display(a);

 return 0;

}

Результаты выполнения этой версии программы выглядят гораздо лучше предыдущих.

Выделение памяти, адресуемой указателем р.

10

Освобождение памяти, адресуемой указателем р.

Как видите, здесь деструктор вызывается только один раз, поскольку при передаче по ссылке аргумента функции display() копия объекта не создается.

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

Возвращение объектов функциями

Если объекты можно передавать функциям, то "с таким же успехом" функции могут возвращать объекты. Чтобы функция могла вернуть объект, во-первых, необходимо объявить в качестве типа возвращаемого ею значения тип соответствующего класса. Во-вторых, нужно обеспечить возврат объекта этого типа с помощью обычной инструкции return. Рассмотрим пример функции, которая возвращает объект.

// Использование функции, которая возвращает объект.

#include

#include

using namespace std;

class sample {

  char s[80];

 public:

  void show() { cout << s << "\n"; }

  void set(char *str) { strcpy(s, str); }

};

// Эта функция возвращает объект типа sample.

sample input()

{

 char instr[80];

 sample str;

 cout << "Введите строку: ";

  cin >> instr;

 str.set(instr);

 return str;

}

int main()

{

 sample ob;

 // Присваиваем объект, возвращаемый

 // функцией input(), объекту ob.

 ob = input();

 ob.show();

 return 0;

}

Перейти на страницу:

Все книги серии Изучайте C++ с профессионалами

Похожие книги