Г. Шилтд - Самоучитель C++ (PDF), страница 14
Описание файла
PDF-файл из архива "Г. Шилтд - Самоучитель C++ (PDF)", который расположен в категории "". Всё это находится в предмете "практика расчётов на пэвм" из 3 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 14 страницы из PDF
В этом коротком примере объект передается функции:#include <iostream>using namespace std;class samp {int i;public:samp(int n) { i = n; }int get_i{) ( return i; ).// Возвращает квадрат o.i.int sqr_it(samp o)return o.get_i{) * o.get__i(};int mainf){samp a(10) , b(2) ;cout « sqr_it(a) « "\n" ;cout « sqr_it(b) « "\n";return 0;В этой программе создается класс samp, который содержит одну целую переменную i.
Функция sqr_it() получает аргумент типа samp, а возвращаемымзначением является квадрат переменной i этого объекта. Результат работыпрограммы — это значения 100 и 4.2. Как уже установлено методом передачи параметров в C++, включая объекты,по умолчанию является передача объекта по значению. Это означает, что внутри функции создается копия аргумента и эта копия, а не сам объект, используется функцией. Поэтому изменение копии объекта внутри функции не влияетна сам объект. Это иллюстрируется следующим примером:/* Запомните, объекты, как и другие параметры, передаются функции позначению и при этом в функции создается копия объекта.
Такимобразом, изменение параметра внутри функции не влияет на объект,используемый в вызове.VГлава 3. Подробное изучение классов89#include <iostream>using namespace std;class samp {int i;public:samp(int n) { i = n; }void set_i(int n) { i = n; }int get_i() { return i; }\./* Заменяет переменную o.i ее квадратом. Однако это не влияет наобъект, используемый для вызова функции sqr_it {}*/void sqr_it(samp о){o.set_i( o.get_i() * o.get_i());cout « "Для копии объекта а значение i равно: " « o.get_i{);cout.« "\n";int main()samp a (10);sqr_it(a); // передача объекта а по значениюcout « "но переменная a.i в функции mainf) не изменилась: ";cout « a.get i(); // выводится 10return 0;}В результате работы программы на экран выводится следующее:Для копии объекта а значение i равно: 100но переменная a . i в функции m a i n ( ) не изменилась: 103.
Как и в случае с переменными других типов, функции может быть переданоне значение объекта, а его адрес. В этом случае функция может изменитьзначение аргумента, используемого в вызове. Например, в рассматриваемомниже варианте программы из предыдущего примера значение объекта, чейадрес используется при вызове функции sqr_it(), действительно меняется./* Теперь функции sqr i t ( ) передается адрес объекта и функция можетизменить значение аргумента, адрес которого используется при вызове.V^include <iostream>using namespace std;90__Самоучитель C++class samp {int i ;public :samp (int n) { i = n; }void set_i(int n) { i = n; }int get_i() ( return i; }/* Заменяет переменную o.i ее квадратом.
Это влияет на объект,используемый при вызове*/void sqr_it (samp *o){o->set_i {o->get_i ( ) * o->get_i ( ) ) ;cout « "Для объекта а значение i равно: " « o->get_i();cout « "\n";int main(){samp a (10) ;sqr_it (fia) ; // функции sqr_it ( } передан адрес объекта аcout « "Теперь значение объекта а в функции main() изменилось:";cout « а . get_i ( ) ; // выводится 100return 0;}Теперь результат работы программы следующий:Для объекта а значение i равно: 100Теперь значение объекта а в функции m a i n ( } изменилось: 1004. Если при передаче объекта в функцию делается его копия, это означает, чтопоявляется новый объект.
Когда работа функции, которой был передан объект, завершается, то копия аргумента удаляется. Возникают два вопроса. Вопервых, вызывается ли конструктор объекта, когда создается его копия? Вовторых, вызывается ли деструктор объекта, когда эта копия удаляется? Ответна первый вопрос может показаться неожиданным.Когда при вызове функции создается копия объекта, конструктор копии не вызывается. Смысл этого понять просто. Поскольку конструктор обычно используется для инициализации некоторых составляющих объекта, он не долженвызываться при создании копии уже существующего объекта. Если бы это было сделано, то изменилось бы содержимое объекта, поскольку при передачеобъекта функции необходимо его текущее, а не начальное состояние.Глава 3. Подробное изучение классов91Однако если работа функции завершается и копия удаляется, то деструкторкопии вызывается. Это происходит потому, что иначе оказались бы невыполненными некоторые необходимые операции. Например, для копии можетбыть выделена память, которую, после завершения работы функции, необходимо освободить.Итак, при создании копии объекта, когда он используется в качестве аргументафункции, конструктор копии не вызывается.
Однако, когда копия удаляется(обычно это происходит при возвращении функцией своего значения), вызывается ее деструктор.Следующая программа иллюстрирует эти положения:^include <iostreara>using namespace std;class samp {int i ;public:samp(int n) (i = n;cout « "Работа конструктора\n";}-samp ( ) { cout « "Работа деструктора\n"; )int get_i() { return i; }1;// Возвращает квадрат переменной o.iint sqr it(samp o)return o.get_i() * o.get_i();int main (){samp a (10);cout « sqr__it(a) « "\n";return 0;}Эта программа выводит следующее:Работа конструктора100Работа деструктораРабота деструктораОбратите внимание, что конструктор вызывается только один раз. Это происходит при создании объекта а.
Однако деструктор вызывается дважды.92Самоучитель C++Первый раз он вызывается для копии, созданной, когда объект а был переданфункции sqr_it(), другой — для самого объекта а.Тот факт, что деструктор объекта, являющегося копией передаваемого функции аргумента, выполняется при завершении работы функции, может статьпотенциальным источником проблем. Например, если для объекта, используемого в качестве аргумента, выделена динамическая память, которая освобождается при его удалении, тогда и для копии объекта при вызоведеструктора будет освобождаться та же самая память. Это приведет к повреждению исходного объекта.
(Для примера см. упражнение 2 данного раздела.) Чтобы избежать такого рода ошибок, важно убедиться в том, что деструктор копии объекта, используемого в качестве аргумента, не вызывает никаких побочных эффектов, которые могли бы повлиять на -исходныйаргумент.Как вы, возможно, уже догадались, одним из способов обойти проблему удаления деструктором необходимых данных при вызове функции с объектом вкачестве аргумента должна стать передача функции не самого объекта, а егоадреса. Если функции передается адрес объекта, то нового объекта не создается и поэтому при возвращении функцией своего значения деструктор невызывается.
(Как вы увидите в следующей главе, в C++ имеется и иное, более элегантное решение этой задачи.) Тем не менее имеется другое, лучшеерешение, о котором вы узнаете, изучив особый тип конструктора, а именноконструктор копий (copy constructor). Конструктор копий позволяет точно определить порядок создания копий объекта. (О конструкторах копий рассказывается в главе 5.)1. Используя класс stack из раздела 3.1, пример 2, добавьте в программу функцию showstack(), которой в качестве аргумента передается объект типа stack.Эта функция должна выводить содержимое стека на экран.2. Как вы знаете, если объект передается функции, создается копия этого объекта. Далее, когда эта функция возвращает свое значение, вызывается деструктор копии.
Вспомнив это, ответьте, что неправильно в следующейпрограмме?// В этой v-P°rPa:Mbie есть ошибка#include <iostream>#include <cstdlib>using namespace std;class dyna jint *p;public:dyna f i n t i ) ;~dyna() ( f r e e ( p ) ; cout « "освобождение памяти\п"; }Глава 3. Подробное изучение классовint get () { return *p; }dyna : : dyna { int i ){p = (int *) malloc {sizeof (int) ) ;if(!p) {cout « "Ошибка выделения памяти\п";exit(l) ;// Возвращает отрицательное значение *ob.pint neg(dyna ob}return -ob.getf);1int main(){dyna о (-10) ;cout « o .
g e t f } « " \ n " ;cout « n e g ( o ) « "\n";dyna o2{20) ;cout « o2.get() « "\n";cout « neg{o2) « "\n";cout « o.get() « "\n";cout « neg(o) « "\n";return 0;3.3. Объекты в качествевозвращаемого значения функцийТак же как объект может быть передан функции в качестве аргумента, онможет быть и возвращаемым значением функций.
Для этого, во-первых,объявите функцию так, чтобы ее возвращаемое значение имело тип класса.Во-вторых, объект этого типа возвратите с помощью обычной инструкцииreturn.94Самоучитель C++Имеется одно важное замечание по поводу объектов в качестве возвращаемого значения функций: если функция возвращает объект, то для хранениявозвращаемого значения автоматически создается временный объект. Послетого как значение возвращено, этот объект удаляется.
Удаление этого временного объекта может приводить к неожиданным побочным эффектам, чтоиллюстрируется в примере 2 этого раздела.РПримеры1. Пример функции с объектом в качестве возвращаемого значения:// Возвращение объекта из функции^include <iostream>^include <cstring>using namespace std;class samp {char s[BO];public:void show() { cout « s « "\n"; }void set(char *str) { strcpyts, str); }// Возвращает объект типа sampsamp input(}{char s[80];samp str;cout « "Введите строку: ";cin » s;str.set (s);return str;int main(){samp ob;// присваивание возвращаемого значения объекту obob = input();ob.show();return 0;Глава3.Подробноеизучениеклассов_95В этом примере функция input() создает локальный объект str и считываетстроку с клавиатуры.
Эта строка копируется в str.s, и затем функция возвращает объект str. Внутри функции main() при вызове функции input() возвращаемый объект присваивается объекту оЪ.2. Следует быть внимательными при возвращении объектов из функций, еслиэти объекты содержат деструктор, поскольку возвращаемый объект выходитиз области видимости, как только функция возвращает его значение. Например, если функция возвращает объект, имеющий деструктор, который освобождает динамически выделенную память, то эта память будет освобожденанезависимо от того, использует ли ее объект, которому присваивается возвращаемое значение, или нет.
Например, рассмотрим неправильную версиюпредыдущей программы:// При возвращении объекта генерируется ошибка#include <iostream>#include <cstring>^include <cstdlib>using namespace std;class samp {char *s;public :samp ( ) { s = ' \ 0 ' ; }~samp() { i f ( s ) f r e e ( s ) ;cout « "Освобождение памяти по адресу sNn"; }void show () { cout « s « "Nn"; }void set (char *str) ;// Загружает строкуvoid samp: : set (char *str)s = (char *} malloc (strlen(str) +1) ;if(!s) {cout « "Ошибка выделенияexit(l) ;strcpy (s, str) ;}// Возвращает объект типа sampsamp input ()(char s[80] ;samp str;cout « "Введите строку: ";cin » s;36__СамоучительC++s t r .