if(Initiallnput != NULL)
{
Buffer = new char [strlen(Initiallnput) + 1];
strcpy(Buffer, Initiallnput);
}
else
Buffer = NULL;
}
2 0 :
// Деструктор
~MyString()
{
cout « "Invoking destructor, clearing up" « endl;
if (Buffer != NULL)
delete [] Buffer;
}
28:
int GetLengthO
{
return strlen(Buffer);
}
33:
const char* GetStringO
{
return Buffer;
}
};
39:
void UseMyString(MyString Input)
{
cout « "String buffer in MyString is " « Input.GetLength();
43:
|
cout « " characters long" «
|
endl;
|
44:
|
|
|
cout « "Buffer contains: " « Input.GetString() « endl;
return;
}
48:
int main()
{
SayHello("Hello_from_String_Class");'>MyString SayHello("Hello from String Class");
// Передать SayHello функции как параметр
UseMyString(SayHello);
55:
return 0;
}
Результат
String buffer in MyString is 23 characters long Buffer contains: Hello from String Class Invoking destructor, clearing up Invoking destructor, clearing up
<отказ, как можно заметить на рис. 9.2>
Конструктор копий 225
РИС. 9.2. Снимок экрана аварийного отказа, произо шедшего при выполнении кода листинга 9.8 (режим отладки среды разработки Visual Studio)
Анализ
Почему класс, который только что прекрасно работал в листинге 9.6, привел к отказу
листинге 9.7? Единственное различие между листингами 9.6 и 9.7 в том, что задача ис пользования объекта S a y H e llo класса M y S t r in g , созданного в функции m a in (), была делегирована функции U s e M y S t r in g (), вызываемой а строке 54. Делегирование работы ггой функции привело к тому, что объект S a y H e llo в функции m a in () копируется в ар гумент параметра I n p u t, используемого в функции U s e M y S t r in g ( ) . Эта копия создается компилятором, поскольку функция была объявлена как получающая параметр I n p u t по значению, а не по ссылке. Компилятор создает двоичную копию простых старых данных, таких, как целые числа, символы и указатели. Таким образом, значение, содержащееся в указателе S a y H e llo . B u f f e r , было просто скопировано в параметр I n p u t , т.е. он теперь указывает на ту же область памяти, что и I n p u t . B u f f e r (рис. 9.3).
РИС. 9.3. Поверхностное копирование объекта SayHello в параметр Input при вы зове функции UseMyString ()
Двоичная копия не обеспечивает глубокого копирования (deep сору) и не распро страняется на указываемую область памяти, поэтому теперь есть два объекта класса M y S t r in g , указывающих на ту же область в памяти. Таким образом, по завершении работы функции U s e M y S t r in g () переменная I n p u t выходит из области видимости и удаляется. При этом вызывается деструктор класса M y S t r in g , и его код в строке 26
: Зак. 3626
226 ЗАНЯТИЕ 9. Классы и объекты
листинга 9.8 освобож дает при помощи оператора d e l e t e память, зарезервированную для буфера. Обратите внимание, что этот вызов оператора d e l e t e объявляет недействи тельной область памяти, на которую есть указатель в объекте S a y H e llo , находящемся
функции m ain (). Когда функция m ain () заверш ается, объект S a y H e llo выходит из области видимости и удаляется. Однако на сей раз строка 26 повторно вызывает опера
тор d e l e t e для адреса области памяти, который уже недействителен (уже освобожден
объявлен недействительным при удалении параметра I n p u t) . Результатом повторного удаления и будет аварийный отказ. Обратите внимание, что сообщ ение режима отладки, представленное на рис. 9.2, упоминает строку 52 (строка 51 в листинге, ведь строки в книге отсчитываются от нуля), поскольку здесь используется объект S a y H e llo , который не был освобожден успешно.
Компилятор в данном случае не смог автоматически обеспечить глубокое ко пирование, поскольку на момент компиляции ему неизвестно ни количество байтов, на которые указывает указатель-член MyString::Buffer, ни харак тер резервирования.
Do'stlaringiz bilan baham: |