// Эта функция возвращает объект типа 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;

}

Возможные результаты выполнения этой программы выглядят так.

Введите строку: Привет

Освобождение s-памяти.

Освобождение s-памяти.

Здесь "мусор"

Освобождение s-памяти.

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

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

Однако ошибки не миновать, если объект, возвращаемый функцией, присваивается объекту ob, поскольку при выполнении присваивания по умолчанию создается побитовая копия. В данном случае временный объект, возвращаемый функцией input(), копируется в объект ob. В результате член ob.s указывает на ту же самую область памяти, что и член s временного объекта. Но после присваивания в процессе разрушения временного объекта эта память освобождается. Следовательно, член ob.s теперь будет указывать на уже освобожденную память! Более того, память, адресуемая членом ob.s, должна быть освобождена и по завершении программы, т.е. во второй раз. Чтобы предотвратить возникновение этой проблемы, необходимо перегрузить оператор присваивания так, чтобы объект, располагаемый слева от оператора присваивания, выделял собственную область памяти.

Реализация этого решения показана в следующей откорректированной программе.

// Эта программа работает корректно.

#include

#include

#include

using namespace std;

class sample {

  char *s;

 public:

  sample(); // обычный конструктор

  sample(const sample &ob); // конструктор копии

  ~sample() {

   if(s) delete [] s;

   cout << "Освобождение s-памяти.\n";

  }

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

  void set(char *str);

  sample operator=(sample &ob); // перегруженный оператор присваивания

};

// Обычный конструктор.

sample::sample()

{

 s = new char('\0'); // Член s указывает на null-строку.

}

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

sample::sample(const sample &ob)

{

 s = new char[strlen(ob.s)+1];

 strcpy(s, ob.s);

}

// Загрузка строки.

void sample::set(char *str)

{

 s = new char[strlen(str)+1];

 strcpy(s, str);

}

// Перегрузка оператора присваивания.

sample sample::operator=(sample &ob)

{

 /* Если выделенная область памяти имеет недостаточный размер, выделяется новая область памяти. */

 if(strlen (ob.s) > strlen(s)) {

  delete [] s;

  s = new char[strlen(ob.s)+1];

 }

 strcpy(s, ob.s);

 return *this;

}

// Эта функция возвращает объект типа 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++ с профессионалами

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