Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Лабораторная работа № 6

Читайте также:
  1. D триггеры, работающие по фронту.
  2. I. ВНЕАУДИТОРНАЯ САМОСТОЯТЕЛЬНАЯ РАБОТА СТУДЕНТОВ
  3. I. ВНЕАУДИТОРНАЯ САМОСТОЯТЕЛЬНАЯ РАБОТА СТУДЕНТОВ
  4. I. ВНЕАУДИТОРНАЯ САМОСТОЯТЕЛЬНАЯ РАБОТА СТУДЕНТОВ
  5. I. ВНЕАУДИТОРНАЯ САМОСТОЯТЕЛЬНАЯ РАБОТА СТУДЕНТОВ
  6. I. ВНЕАУДИТОРНАЯ САМОСТОЯТЕЛЬНАЯ РАБОТА СТУДЕНТОВ
  7. I. ВНЕАУДИТОРНАЯ САМОСТОЯТЕЛЬНАЯ РАБОТА СТУДЕНТОВ

Цель работы - познакомиться с использованием ссылок, конструктор копий в программах на С++. Для этого надо разобрать приведённые в работе листинги – получить результаты для каждого задания и письменно их объяснить.

Передача функции аргументов по ссылке

Функции имеют два ограничения: когда ар­гументы передаются как значения, они теряют связь с исходными данными, а возвра­щать функция может только одно значение.

Задание 1. Передача данных по значению

#include <vcl.h>

# include <iostream.h>

#include <conio.h>

using namespace std;

void swap(int x, int y);

 

int main(int argc, char* argv[])

{ int x = 5, y = 10;

cout << "Main. Before swap, x: " << x << " y: "

<< y << "\n";

swap(x,y);

cout << "Main. After swap, x: " << x << " y: "

<< y << "\n";

getch();

return 0;

}

void swap(int x, int y)

{

int temp;

cout << "Swap. Before swap, x: " << x << " y: "

<< y << "\n";

temp = x;

x = y;

y = temp;

cout << "Swap. After swap, x: " << x << " y: "

<< y << "\n";

}

Эта программа инициализирует в функции main() две переменные, а затем переда­ет их функцииswap(), которая, казалось бы, должна поменять их значения. Но переменные х и у, переданные функции swap(), по значению, являются локальными копиями этих переменных, созданных непосредственно в функции. Чтобы решить проблему, необходимо передать значения переменных x и y по ссылке. В языке С++ передача данных по ссылке осуществляется двумя способами: с помощью указателей и с помощью ссылок.

Задание 2. Передача данных по ссылке с помощью указателей

#include <vcl.h>

# include <iostream.h>

#include <conio.h>

using namespace std;

void swap(int* x, int* y);

int main(int argc, char* argv[])

{ int x = 5, y = 10;

cout << "Main. Before swap, x: " << x << " y: "

<< y << "\n";

swap(&x,&y); //Передаются адреса переменных

cout << "Main. After swap, x: " << x << " y: "

<< y << "\n";

getch();

return 0;

}

void swap(int* px, int* py)

{

int temp;

cout << "Swap. Before swap, *px: " << *px << " *py: "

<< *py << "\n";

temp = *px;

*px = *py;

*py = temp;

cout << "Swap. After swap, *px: " << *px << " y: "

<< *py << "\n";

}

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

Задание 3. Передача данных по ссылке с помощью ссылки

#include <vcl.h>

# include <iostream.h>

#include <conio.h>

using namespace std;

void swap(int& x, int& y);

int main(int argc, char* argv[])

{ int x = 5, y = 10;

cout << "Main. Before swap, x: " << x << " y: "

<< y << "\n";

swap(x,y);

cout << "Main. After swap, x: " << x << " y: "

<< y << "\n";

getch();

return 0;

}

void swap(int& rx, int& ry)

{

int temp;

cout << "Swap. Before swap, rx: " << rx << " ry: "

<< ry << "\n";

temp = rx;

rx = ry;

ry = temp;

cout << "Swap. After swap, rx: " << rx << " y: "

<< ry << "\n";

}

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

При каждой передаче объекта в функцию как значения создается копия этого объ­екта. При каждом возврате объекта из функции

создается еще одна копия. Эти объекты копируются в стек, и на этот процесс расходуется время и память. Для таких маленьких

объектов, как встро­енные целочисленные значения, цена этих расходов незначительна. Но для больших пользовательских

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

При работе с большими объектами постоянные вызовы конструктора и деструкто­ра могут оказать ощутимое влияние на скорость работы программы и использование памяти компьютера. Для подтверждения этой идеи в листинге задания 4 создается пользовательский объект SimplеCat, после чего вызываются две функ­ции. Первая функция принимает объект Саt как значение, а затем возвращает его как значение. Вторая же функция принимает не сам объект, а лишь указатель на него, и возвращает также указатель. Реальный объект имел бы большие размеры и занял бы больше памяти, но и этого примера вполне достаточно, чтобы показать, насколько часто вызываются конструктор копий и деструктор.

Задания 4. Демонстрация вызова конструктора копий

#include <vcl.h>

# include <iostream.h>

#include <conio.h>

using namespace std;

class SimpleCat

{

public:

SimpleCat (); // Конструктор

SimpleCat (SimpleCat &); // Конструктор копий

~SimpleCat(); // Деструктор

};

SimpleCat::SimpleCat()

{

cout << " Constructor... \n";

}

SimpleCat::SimpleCat (SimpleCat&)

{

cout << " * Copy Constructor... \n";

}

SimpleCat:: ~SimpleCat()

{

cout << " Destructor... \n";

}

SimpleCat FunctionOne (SimpleCat theCat);

SimpleCat* FunctionTwo (SimpleCat* theCat);

int main(int argc, char* argv[])

{

cout << "Making a cat...\n";

SimpleCat Frisky;

cout << " Calling FunctionOne... \n";

FunctionOne(Frisky);

cout << " Calling FunctionTwo... \n";

FunctionTwo(&Frisky);

cout << " Calling FunctionOne... \n";

FunctionOne(Frisky);

getch();

return 0;

}

SimpleCat FunctionOne (SimpleCat theCat)

{

cout << " Function One.Returning... \n";

return theCat;

}

SimpleCat* FunctionTwo (SimpleCat* theCat)

{

cout << " Function Two.Returning... \n";

return theCat;

}

Конструктор копий

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

В результате поверхностного копирования создаются точные копии значений всех переменных-членов одного объекта в другом. Указатели в обоих объектах будут ука­зывать на одну и ту же область памяти. Глубокое копирование переносит значения, на­ходящиеся в динамической памяти, во вновь созданные участки.

Задания 5. Создание конструктора копий с глубоким копированием

#include <vcl.h>

# include <iostream.h>

#include <conio.h>

using namespace std;

class Cat

{ private:

int *itsAge;

int *itsWeight;

public:

Cat (); // конструктор по умолчанию

Cat (const Cat &); // конструктор копий.

~Cat();

int GetAge() const {return *itsAge;}

int GetWeight() const {return *itsWeight;}

int SetAge(int age) {*itsAge = age;}

};

Cat::Cat()

{

itsAge=new int;

itsWeight=new int;

*itsAge=5;

*itsWeight=9;

}

Cat::Cat (const Cat& rhs)

{

itsAge=new int;

itsWeight=new int;

*itsAge=rhs.GetAge(); // Openning access

*itsWeight=*(rhs.itsWeight); // Closeing access

}

Cat::~Cat()

{

delete itsAge;

itsAge=0;

delete itsWeight;

itsWeight=0;

}

int main(int argc, char* argv[])

{

Cat frisky;

cout << "frisky's age: " << frisky.GetAge() << endl;

cout << "Setting frisky to 6... \n";

frisky.SetAge(6);

cout << "Create object(cat) boots from frisky \n";

Cat boots(frisky);

cout << "frisky's age: " << frisky.GetAge() << endl;

cout << "boots's age: " << boots.GetAge() << endl;

cout << "Setting frisky to 7... \n";

frisky.SetAge(7);

cout << "frisky's age: " << frisky.GetAge() << endl;

cout << "boots's age: " << boots.GetAge() << endl;

getch();

return 0;

}

Объявленые две переменные-члена в создаваемом классе, представляют собой указатели на целочисленные значения. Обычно так не поступают, хранить в переменных-членах класса указатели на тип int абсолютно бессмысленно, здесь это сделано исключительно для примера манипулирования переменными-членами в динамической памяти.

Стандартный конструктор выделяет в динамической памяти об­ласть для двух переменных типа int и инициализирует их.

В конструкторе копий обратите внимание на параметр rhs. В качестве параметра конструктора копий принято использовать объект rhs, название ко­торого является сокращением от right-hand side (стоящий справа).

Конструктор копий работает следующим образом: выделяются области в ди­намической памяти, новым областям присваиваются значения существующего объекта класса САТ.

Параметр rhs, передаваемый в конструктор копий в качестве постоянной ссылки, соответствует объекту классу САТ. Являясь объектом класса САТ, rhs содержит все пере­менные-члены, присущие объекту класса САТ.

Каждый объект класса САТ может получать доступ к закрытым переменным-членам лю­бого другого объекта класса САТ, но обычно для этого принято создавать открытые методы доступа. Функция-член rhs.GetAge(), например, возвращает значение переменной- члена itsAge объекта rhs.

Когда объекты класса САТ выйдут из области действия, автоматически будут вызва­ны их деструкторы. Для обоих указателей, itsAge и itsWeight, выполняется оператор delete, освобождая выде­ленную для них память. Кроме того, в целях безопасности обоим указателям при­своено значение NULL.


Дата добавления: 2015-11-26; просмотров: 90 | Нарушение авторских прав



mybiblioteka.su - 2015-2024 год. (0.019 сек.)