Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Comp (double x)




{ real = x; imag =0.0; }

позволяет удалить все дополнительные операции-функции, оставив только одну с прототипом:

friend comp operator +(comp, comp);

В этом случае целый операнд выражения 6+ее автоматически преобразуется к типу double, а затем конструктор формирует комплексное число с нулевой мнимой частью. Далее выполняется операция-функция

operator +(comp(double(6),double(0)), ЕЕ)

Вместо включения в класс дополнительного конструктора с одним аргументом можно в заголовке единственного конструктора ввести умалчиваемое значение второго параметра:

compl(double r, double i = 0.0) { Re=r; Im = i; }

Теперь каждое выражение с операцией ' + ', в которое входит, кроме объекта класса comp, операнд одного из стандартных типов, будет обрабатываться совершенно верно. Однако такое умалчивание является частным решением и не для всех классов пригодно.

В отличие от всех других унарных операций операции ++ и --имеют, кроме префиксной формы еще и постфиксную. Это привело к особенностям при их перегрузке. В начальных версиях языка Си++ при перегрузках операций ++ и -- не делалось различия между постфиксной и префиксной формами.

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

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

 

Особенности реализации конструктора копии, деструктора и оператора-функции operator=() для классов имеющих компонентные данные в виде указателей

Если в структурированном типе определены компонентные данные в виде указателей, то для того чтобы объекты этого типа в программе работали правильно необходимо в 99% случаев обязательно явно написать код конструктора копии, деструктора и оператора-функции operator=().

Рассмотрим пример, приведённый ниже

Struct comp{

int Re, Im;

Comp(){Re= 0; Im = 0;} //конструктор по умолчанию

Comp(int r, int i){Re = r; Im = i;} // конструктор

};

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

comp (comp& T) { Re = T.Re; Im=T.Im;}//Конструктор копии

const comp& operator=(const comp& T){ // оператор функция

Re = T.Re; Im = T.Im; return *this;}

~comp(){} //деструктор

В принципе, поскольку компонентные данные Re и Im не являются указателями, то сгенерированные компилятором функции будут работать правильно, и программисту нет необходимости писать их самостоятельно. Картина сильно изменится, если определить класс comp следующим образом:

Srtuct comp{

int* Re, *Im;

Comp(){Re=new int; Im=new int;;*Re = 0; *Im = 0;}

Comp(int r, int i){Re=new int; Im=new int;*Re = r; *Im = i;}

};

Для этого примера компилятор по умолчанию сгенерирует те же самые функции с тем же самым содержанием.

comp (comp& T) { Re = T.Re; Im=T.Im;}//Конструктор копии

const comp& operator=(const comp& T){ // оператор функция

Re = T.Re; Im = T.Im; return *this;}

~comp(){} //деструктор

Только в данном случае эти функции будут работать неправильно, и поэтому программисту их необходимо будет написать самостоятельно. Поясним, в чём заключается их неправильность, и покажем, как правильно реализовать эти функции.

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

~comp(){delete Re; delete Im;}

Во-вторых, если у нас имеется код вида: Comp A(1,1), B(2,2); A=B;, то после выполнения операции присваивания с помощью сгенерированной по умолчанию операцией-функцией operator=() объекты A и B становятся взаимозависимыми, и это чревато трудно обнаруживаемыми ошибками. Сказанное поясним с помощью таблицы, приведённой ниже

 

До операции присваивания После операции присваивания
A.Re à 0x00000001 A.Im à 0x00000001   B.Re à 0x00000002 B.Im à 0x00000002 0x00000001 0x00000001   B.Re à 0x00000002 ßA.Re B.Im à 0x00000002 ß A.Im

 

 

До операции присваивания указатели Re и Im объектов A и В указывали на разные фрагменты динамической памяти, которые содержали числа 1 и 2. После выполнения операции присваивания, с одной стороны, на фрагменты динамической памяти, содержащие единицы не указывает ни один указатель, а это значит, что мы не сможем эту память освободить с помощью операции delete, т.е. произошла утечка памяти. С другой стороны, на фрагменты памяти с 2 указывают как указатели объекта A, так и В, а это значит, что эти объекты стали взаимозависимыми.

Для того чтобы избежать такой ситуации необходимо программисту самостоятельно определить операцию функцию operator=() следующим образом:

const comp& operator=(const comp& T){ // оператор функция

*Re = T.*Re; *Im = T.*Im; return *this;

}

Т.е. необходимо копировать не значения указателей, а значения, хранящиеся во фрагментах динамической памяти, на которые указывают эти указатели. При таком способе реализации operator=() объекты A и B останутся независимыми, что и показано в таблице, приведённой ниже

До операции присваивания После операции присваивания
A.Re à 0x00000001 A.Im à 0x00000001   B.Re à 0x00000002 B.Im à 0x00000002 A.Re à 0x00000002 A.Im à 0x00000002   B.Re à 0x00000002 B.Im à 0x00000002

 

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

void fun(comp B){ }

Comp A(1,1); // неявно вызывается конструктор

fun(A); // неявно вызывается конструктор копии и после выхода из функции деструктор

A.*Re = 3; // Ошибка времени выполнения

 

До вызова функции fun Во время вызова функции fun После выхода из функции fun
A.Re à 0x00000001 A.Im à 0x00000001 B.Re à 0x00000001 ßA.Re B.Im à 0x00000001 ß A.Im A.Reà ошибочный указатель A.Imà ошибочный указатель

 

До вызова функции fun() существует объект A, указатели которого указывают на фрагменты динамической памяти с единицами. При вызове функции fun() конструктор копии создаёт локальный объект B и делает его зависимым от объекта A. После выхода из функции fun() вызывается деструктор, который уничтожает локальный объект B, освобождая с помощью операции delete фрагменты динамической памяти, на которые указывают указатели Re и Im объекта В, но на эти же фрагменты указывают и указатели объекта A. В результате объект A становится повреждённым. Его указатели становятся не действительными, и при попытке записать какое-либо значение в память по адресу в этих указателях возникает ошибка времени выполнения.

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

comp (comp& T) {

Re = new int; Im = new int; *Re = T.*Re; *Im = T.*Im;

}

При такой реализации зависимость между фактическим А и формальным В параметрами не возникает, что и показано в таблице.

До вызова функции fun Во время вызова функции fun После выхода из функции fun
A.Re à 0x00000001 A.Im à 0x00000001 B.Re à 0x00000001 B.Im à 0x00000001 A.Re à 0x00000001 A.Im à 0x00000001 A.Re à 0x00000001 A.Im à 0x00000001

 

У локального параметра В свои фрагменты динамической памяти, которые никак не связаны с фрагментами фактического параметра A.

 

 




Поделиться с друзьями:


Дата добавления: 2014-01-11; Просмотров: 383; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.014 сек.