Теор_часть-2лаба (Вторая лаба), страница 2
Описание файла
Текстовый-файл из архива "Вторая лаба", который расположен в категории "". Всё это находится в предмете "программирование" из 3 семестр, которые можно найти в файловом архиве МПУ. Не смотря на прямую связь этого архива с МПУ, его также можно найти и в других разделах. Архив можно найти в разделе "лабораторные работы", в предмете "программирование" в общих файлах.
Просмотр 2 страницы текстового-файла онлайн
// Этот фрагмент эквивалентен следующему:
int x, *y;
y=&x; // y - ссылка на х
x = 0;
*y = 10; // означает, что х тоже равен 10
Внимательно сравнивая эти два фрагмента, можно отметить, что при определении переменной y в виде ссылки (&y) в первом варианте это выглядит как обычная целая переменная, и то, что фактически она является указателем на переменную x, скрыто синтаксисом С++.
Можно указывать амперсанд просто для создания дополнительного имени переменной, но наиболее важно его использование при вызове функций. Рассмотрим прототип конструктора класса MyStr:
MyStr (MyStr &wstr);
Этот конструктор имеет один параметр wstr, типом которого является ссылка на объект класса MyStr. Проще говоря, чтобы создать копию объекта, конструктор копирования получает ссылку на объект и через нее - доступ ко всем членам этого объекта.
ВНИМАНИЕ! Разница между передачей параметра как указателя передачи его по ссылке заключается в последовательности действий. При передаче указателя данные уже определены и для них определяется адрес (указатель). При передаче по ссылке сначала определяется адрес, а затем по этому адресу формируются данные.
С этой точки зрения всё, что нужно помнить об операторе ссылки - это то, что параметр берётся как значение, а не указатель, несмотря на то, что он передаётся по ссылке. Конструктор копирования в этом случае будет выглядеть так:
MyStr::MyStr(MyStr &wstr)
{
char *tmpstr;
int l;
tmpstr = wstr.getptr();
l = wstr.length();
pstr = new char [l+1];
strcpy (pstr, tmpstr);
lstr = l;
}
Этот конструктор во многом подобен конструктору MyStr(char*). Принципиальное отличие заключается в том, что конструктор должен сначала выделить строку и её длину во вспомогательных переменных, а затем уже с их помощью произвести копирование.
Заголовок конструктора может на первый взгляд показаться со странным из-за троекратного вхождения в него идентификатора MyStr, но каждая употребление MyStr в заголовке конструктора копирования играет свою роль: первое указывает на класс для которого определяется конструктор, второе - на имя конструктора, третье - на тип аргумента.
Защита параметра изменений.
Наконец, осталось уточнить одну небольшую деталь. Обычно передача параметра по ссылке употребляется в том случае, если нужно его изменять. Однако, в нашем случае изменение объекта, копия которого создаётся с помощью конструктора копирования, крайне нежелательно для таких случаев (C++ предлагется использование из ключевого слова const при объявлении параметров.
Итак, окончательный вариант конструктора копирования выглядит так:
MyStr::MyStr(const MyStr &wstr)
{
//...
//тело конструктора сохраняется
}
Теперь конструктор копирования не может изменить значение не одного из членов объекта wstr, т.к. любая попытка присвоить значение объекта wstr или любому из его данных-членов вызовет ошибку на этапе компиляции. Но существует ещё одно слабое место у этого конструктора: вызов функции getptr и length для объекта wstr. Как обеспечить неизменность параметра или его членов при вызове этих функций? Решением проблемы является объявление функций getptr и length функциями-константами.
char *MyStr::getptr(void)
const
{
return (pstr);
}
int MyStr::length(void)
const
{
return(lstr);
}
Объявление функций константами делает безопасным их вызов для объектов, объявленных конструкторами, и предохраняет последние от каких-либо изменений в функциях, использующих эти объекты в качестве параметров.
Примеры конструкторов.
Создадим несколько конструкторов для класса графических точек.
// пример 30
class GraphPoint
{
private:
int x, y, color;
public:
GraphPoint (); // конструктор по умолчанию
GraphPoint (int wx, int wy, int wcolor);
GraphPoint (const GraphPoint &wgp);
/*конструктор копирования*/
int getcolor(void);
void setnewcolor(int wcolor);
};
Класс GraphPoint значительно проще класса MyStr. Он не требует динамичного распределения и перераспределения памяти, а также её освобождения, поэтому конструктор по умолчанию может быть пустым. Он включается в определение класса только для того, чтобы избежать ошибок при неявном вызове конструктора этого типа. Таким образом, последний имеет вид:
GraphPoint::GraphPoint()
{
}
Следующий конструктор более интересен, он инициализирует на экране монитора графическую точку с координатами wx, wy, цвета wcolor.
GraphPoint::GraphPoint(int wx, int wy, int wcolor)
{
x = wx;
y = wy;
color = wcolor;
}
Наконец, конструктор копирования инициализирует объект, копируя в него данные-члены другого объекта этого класса. Объект, копия которого создаётся, передаётся конструктору в качестве параметра. Этот конструктор может отсутствовать в определении класса, так как он выполяется те же действия (почленное копирование), что и конструктор копирования поддерживаемый компилятором.
GraphPoint::GraphPoint(const GraphPoint &wgp)
{
x = wgp.x;
y = wgp.y;
color = wgp.color;
}
Далее приводятся примеры вызова каждого из описанных выше конструкторов:
GraphPoint gp1;
GraphPoint gp2(100, 50, YELLOW);
GraphPoint gp3 (gp2);
Вызов конструкторов и конверсия.
Итак, мы разобрали механизм вызова конструкторов во время работы программы. Что происходит при объявлении объектов с их одновременной инициализацией? Рассмотрим следующее объявление переменных.
// Пример 31
MyStr str1 ("Привет!");
MyStr str2 = str1;
MyStr str3 = "Привет!";
Понятно, что в первом объявлении вызывается конструктор MyStr (char*). Во втором объявлении также вызывается конструктор (конструктор копирования), и эта строка эквивалента показанной ниже строке не только по результату работы, но и по способу её выполнения:
MyStr str2 (str1);
При выполнении обеих строк вызывается один и тот же конструктор копирования MyStr (MyStr &).
В С++ присваивание и инициализация выполняются совершенно по-разному, несмотря на внешнее сходство операторов (=).
ВНИМАНИЕ! За исключением объявления переменных знак равенства всегда означает присваивание. При объявлении переменных он указывает на инициализацию, которая вызывает соответствующий типу переменной конструктор, а не оператор присваивания.
Программист может определить поведение оператора присваивания, но это не совсем тоже самое, что конструктор копирования. Иногда (но далеко не всегда) они действительно выполняют одни и те же действия. (Главным отличием является то, что конструктор копирования должен, как правило, сначала создать объект, а затем присваивать его членам начальное значения.) Аналогичным образом в последней строке примера 31вызывается конструктор MyStr (char*), и она эквивалентна 1-ой строке это же примера.
Существует ещё один случай, когда вызываются конструкторы и при преобразовании параметра какого-либо типа в класс. Однако, данная процедура выполяется только в том случае, если функция имеет ровно один параметр и существует конструктор класса из переменной этого типа.
// Пример 32
/*Прототип функции удаляющей пробелы из строки,
являющейся членом класса MyStr*/
MyStr Delete_Spaces (MyStr wstr);
// фрагмент вызывающей функции
MyStr str1;
str1 = Delete_Spaces ("123");
Последняя строка примера выполняет вызов конструктора MyStr (char*) для создания объекта класса MyStr и строки "123". Результирующий объект класса MyStr передаётся функции Delete_Spaces, и далее в нём удаляются пробелы.