Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
С++.docx
Скачиваний:
19
Добавлен:
10.06.2015
Размер:
95.05 Кб
Скачать

45. Вызовы конструкторов в процессе работы программы.

Прежде всего, нужно учесть, что к конструкторам нельзя обращаться явно. При этом выбор конкретного конструктора класса осуществляется в соответствии с форматом определенного объекта. Если класс определен без явного определения конструктора, то компилятор неявно создаст два конструктора.

Для иллюстрации будем использовать класс определенный в пункте 2.1.

Конструктор без параметров вызывается при следующих форматах определения класса:

<имя класса> <имя объекта>; // (1)

<имя класса> <имя массива объекта> [<размер>]; // (2)

<указатель на объекты класса> = new <имя класса>; // (3)

<указатель на объекты класса> = new <имя класса> [<размер>]; // (4)

На практике эти форматы реализуются так:

TVektClass V; // (5)

TVektClass*(указатель) Ptr V = new TVektClass; // (6)

TVektClass* Ptr V = new TVektClass [5]; // (7)

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

TVektClass ( ) {for (int i=0; i<10; i++) {FVekt [i] = 1;}} // (8)

В результате если определение строки 12 будет заданно глобально, то элементы поля FVekt получат значения указанные в конструкторе, а поля FSvertka и FKol получат значения 0, если же строка 12 локальна, то FSvertks и FKol будут иметь случайные значения. Указанные особенности распространяются на все конструкторы объявленные в классе.

Конструктор общего вида все значения по умолчанию, в этом же конструкторе применен инициализатор:

double FVektmas [10]; // (9) это глобальный массив, инициализируемый нулями, будем его использовать для определения по умолчанию 1-го параметра конструктора

TVektClass (double* mas = FVektmas, int nvekt = 10, double fsv = 0, int fk = 0) // (10)

:FSvertka (fsv), FKol (fk) // (11) инициализатор

{for (int) = 0; i < nvekt; i++) {FVekt [i] = mas [i]}}

В этом примере поля базовых типов FSvertka и FKol инициализированы с помощью инициализатора (строка 11), а поле-массив FVekt инициализирован в теле конструктора. В том и в другом случае инициализированные значения передаются через параметры конструктора, реальная длина массива (вектора) задана дополнительным параметром nvekt. Если в классе определен такой конструктор, то именно он будет вызван при создании объектов. При этом все поля объектов будут инициализированы значением по умолчанию, заданным в заголовке конструктора. Очевидно, что в 1-м классе нельзя одновременно определить конструктор без параметров и конструктор со всеми параметрами, имеющих значения по умолчанию. Будут нарушены правила создания перегруженной функции.

Пользуясь приведенным примером, поясним семантику термина конструктор по умолчанию. В строчках 4 – 7 в опер. опред. объектов нет внеш. синтаксич. призн. конст. классов, именно это и делает логичным термин «конструктор умолчания», который относится к явным или неявным конструкторам без параметров, либо к созданному явно конструктору общего вида, так как именно эти конструкторы вызываются по умолчанию.

46. Конструкторы приведения типов (из п. 2.3.3 лекций выбрать часть материала, иллюстрирующего особенности определения конструкторов приведения типов и их вызова при определении объекта. Детальное определение класса TTochka_v_pole давать не нужно).

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

Что бы проиллюстрировать такой пример, приведем пример материальной точки:

  • точка движется в 3-х мерном пространстве ограниченным плоской подстилающей поверхностью, нормаль которой противоположна вектору поля

  • поле полностью описывает ускорение свободного падения, независящее от координат точки

  • взаимодействие точки с подстилающей поверхностью происходит мгновенно

  • никаким другим силам кроме силы тяжести и сил упругого столкновения с подстилающей поверхностью тоска не подвержена

  • t0 задано координатами и вектором скорости точки

Рассмотрим простейшую задачу, где надо рассчитать:

  1. момент 1-го столкновения с подстилающей поверхностью

  2. максимальную высоту подъема точки над поверхностью и 1-й момент выхода на эту высоту

Будем полагать, что справедливо следующее начальное условие:

t0 точка находится над поверхностью, а вектор начальной скорости коллинеарен нормали подстилающей поверхности, тем самым задача превращается в одномерную, а именно точка будет двигаться по прямой перпендикулярно подстилающей поверхности, либо вверх до максимальной высоты, а потом вертикально вниз до столкновения с поверхностью, либо вниз до столкновения, а потом вверх до максимальной высоты.

И так, мы должны разработать класс, включающий 3 подсистемы: однородное гравитационное поле, подстилающую систему и материальную точку.

Для того чтобы расширить систему и в частности проиллюстрировать конструктор, помимо массы mp точка характеризуется цветом(color) и номером(num) и при этом:

  • цвет точки определяет значение падения: зеленые на Земле, белые на Луне, красные на Марсе

  • для точек разных цветов установить различные разрешенные диапазоны значений модуля вектора начальной скорости (направление вектора цветом не регламентировано)

Если заданное значение начальной скорости выходит за пределы, то конструктор должен позаботится о подходящем корректирование исходных данных. Номер точки генерируется автоматически в процессе выполнения программы, при создании очередного объекта ему должен присваиваться уникальный номер, кроме того нам понадобится статическое поле, которое считает сколько создано объектов за всю работу программы.

Приведем коды вспомогательных типов и переменных, а также коды разработанного класса:

typedef double odn_mas [3]; // мы определили тип данных для задания коордлинат точки и ее вектора скорости

odn_mas ARO = {0, 0, 10}; // дежурный вариант координат точки

odn_mas AVO = {0, 0, 1}; // дежурный вариант вектора скорости точки

enum color {green, red, while}

class TTochka_v_pole { // класс

private: // объявлены закрытые поля

double FRO [3]; // поле хранящее начальные координаты

double FVO [3]; // начальный вектор скорости

double Vmin Vmax [3] [2]; // допустимые диапазоны для точек разных цветов (например Vmin Vmax [i] [0] – минимально допустимый модуль для точки i-того цвета; Vmin Vmax [i] [1] – максимально допустимый модуль для точки i-того цвета)

double EMp; // хранится масса точки

color FMcolor; // номер точки

double Fg; // ускорение свободного падения

Static int Fcanter; // количество созданных точек

public: // далее идут общедоступные поля

double FVO modul;

double Fmax Z; // максимальная высота точки

double FT_max Z; // 1-й момент выхлода на максимальную высоту

double FT_ZO; // 1-й момент столкновения с подстилающей поверхностью

// Далее определим прототип конструктора 1 общего вида со всеми параметрами по умолчанию

TTochka_v_pole (odn_mas RO = ARO, odn_mas VO = AVO, int Mp = 11, color c = green, double g=10);

// Объявляем прототип конструктора 2 и его класс приведения типов

TTochka_v_pole(color c, odn_mas RO = ARO, odn_mas VO = AVO, int Mp =1);

// Объявим прототип вспомогательного типа, выводим в консольное окно значения полей

int Vivod_v_consol ( );

// Прототип метода расчета максимальной высоты точки 1-го момента выхода на эту высоту и 1-го момента столкновения точки

int krit_point ( );

}; // конец оператора класса

Далее приведем полное определение конструктора (возможно это не нало :D)

// Определим конструктор 1 вне класса, инициализация не используется, инициализация полей осуществляется в теле конструктора через параметры конструктора

TTochka_v_pole::TTochka_v_pole (odn_mas RO, odn_mas VO, int Mp color c, odn lle g);

{ FVOmodul = 0;

for (int i = 0; I <3; i++)

{

FRO [i] = RO [i]; FVO [i] = VO [i];

FVOmodul = FVOmodul + VO [i] * VO [i];

}

FVOmodul = sqrt (FVOmodul);

FMp = Mp; // знач. парам.

FMcolor = c;

Fcounter ++;

Fnum = Fcounter;

// Зашьем допустимые границы в класс (можно было бы организовать ввод пользователем программы)

Vmin Vmax [0] [0] = Vmin Vmax [1] [0] = Vmin Vmax [2] [0] = 0; -//-//- [0] [1] = 5; -//-//- [1] [1] = 10; -//-//- [2] [1] = 20;

// Допустимость скорости задается пользователем и при необходимости корректируется

int zn = (FVO [i] > 0)&1: -1;

if (FVOmodul < Vmin Vmax [c] [0]) {FVO [0] = FVO [1] = 0; FVO [2] = Vmin Vmax [c] [0] * zn;}

if (-//-//- > -//-//- [1]) {-//-//- Fg =g;}

}

// Определим конструктор 2 вне класса, данный конструктор устроен как конструктор 1, отличается только цветом точки

TTochka_v_pole:: TTochka_v_pole (color c, odn_mas VO, int Mp)

{ // до следующего комментария идентичные операторы

// далее уст. Ускорение свободного падения

{switch (c) {

case green: Fg = 10; break;

case red: Fg = 5; break;

case while: Fg = 2;

}

} // далее дадим псевдокоды вспомогательного метода

// определим вне класса вспомогательный метод, выведем в консольное окно значение полей

int TTochka_v_pole:: Vivod_v_consol ( )

{ < … cout

return 0;}

47. Конструкторы копирования. Побитовое (поверхностное) копирование, опасности связанные с таким копированием. Глубокое копирование, особенности работы с ресурсоемкими объектами (имеющими динамически создаваемые поля) (из п. 2.3.4 лекций выбрать часть материала, необходимую для раскрытия перечисленных вопросов).

Проиллюстрируем работу конструктора копирования, причем будем использовать конструктор который неявно создан компилятором.

Конструктор копирования вызывается неявно:

  • при передаче объекта в функцию по значению

  • при возврате объекта

Если конструктор копирования неявно автоматически созданный компилятором полностью и корректно выполняет копирование одного объекта в область памяти другого, то нет необходимости создавать свой конструктор копирования. Однако автоматически созданный конструктор копирования осуществляет простое побитовое копирование, при этом если у объекта имеющиеся поля являются указателями или ссылками на динамически созданную переменную, то автоматически созданный конструктор скопирует только адрес, но не область на которую ссылается указатель, так как он ее не увидит. Поэтому иногда применяют термин поверхностное копирование. Таким образом сложную ситуацию, когда поля – указатели 2-х объектов будут ссылаться на одну и туже область памяти, но может оказаться, что это не соответствует логике моделируемого алгоритма. Более того может образоваться «висячая ссылка», а именно если один из объектов уничтожит область памяти, то его указатель получит значение 0, а именно адрес области которая уже не принадлежит программе и при попытке обращения возникнет фотальная ошибка, а значит произойдет аварийная остановка программы.

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

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

Модифицируем класс TTochka_v_pole следующим образом:

Char* Fname1;

static int FLivePoint; // счетчик живых, то есть создается неуничтожимая точка

TTochka_v_pole (char* name0, color, odn_mas RO = ARO, odn_mas VO = AVO, name_mas name 2 = STD name, int Mp = 1)

// В заголовке используются следующие обозначения:

typedef char name_mas [20]; // определяем новый тип name_mas из 20 символов

name_mas STD name = {‘B’, ‘Л’, ‘Н’, ‘Я’};

// Дадим теперь определение конструктора 3 вне класса

TTochka_vg_pole::TTochka_v_pole (char* name0; char c; odn_mas RO, odn_mas VO, name_mas name2, int Mp);

{…

<здесь конструктор 2>

FLive Point++;

// Далее формируем динамическую область памяти для хранения имени точки

Fname1 = new char [stdlen (name0) + 1]; // формируем динамический массив; +1 терминальный ноль

str cpy (Fname1, name0); // копируем передаваемое в конструктор строковое представление имени в созданную динамическую область

// Теперь создадим свой конструктор копирования, который правильно будет копировать данные объекта в другой объект, создадим при этом свою динамическую ячейку для нового объекта

TTochka_v_pole (const TTochka_v_pole & p); // ссылка на объект класса TTochka_v_pole

// Дадим теперь полное определение этого конструктора вне класса; в других конструкторах задание имени может не предусматриваться, а это значит, что по логике алгоритма будут обработаны безымянные точки, а значит, этот факт нужно предусмотреть, например если копируемый объект имеет безымянную точку, то есть динамическая область для хранения имени не создана, то и в новом объекте этого делать не надо, такая логика алгоритма поддерживается в следующем конструкторе копирования

TTochka_v_pole:: TTochka_v_pole (const TTochka_v_pole & p)

{if (P.Fname1) { // если адрес ≠ 0

Fname1 = new char (str len (P.Fname1) +1}; // тогда создается динамическая область и для нового объекта

Strcpy (Fname1, P.Fname1):} // и копируем туда имя объекта

else Fname1 = 0; // иначе поле указатель нового объекта дает 0

// Далее обработаем счетчики

Fcouter++;

Fnum = Fcounter;

FLivePoint++;

// Далее копируем остальные поля

for (int i=0; I < 3; i++) {FRO [i] = P.FRO [i]; FVO [i] = P.FVO [i];}

}; // конец конструктора

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]