А.В. Столяров - Введение в язык Си++ (1114949), страница 6
Текст из файла (страница 6)
Напомним, что компилятор считает конструктором функцию-членкласса (или структуры), имя которой совпадает с именем класса (илиструктуры). Благодаря свойству перегрузки функций в С и + + мы можем ввести в одной области видимости несколько функций с одним и24тем же именем; это относится, разумеется, и к конструкторам. Главное,чтобы функции, имеющие одинаковые имена (в данном случае — конструкторы), различались количеством и/или типом параметров. Итак,добавим в класс Complex ещё один конструктор:c la s s Complex {double r e , im;p u b lic :Complex(double a_re, double a_im){ re = a_re; im = a_im; }Complex() { re = 0; im = 0; }I I ....Теперь мы можем описывать переменные типа Complex, не задавая никаких параметров.
Поэтому конструктор, имеющий пустой список параметров, называют к о н с т р у к т о р о м по умолчанию. Его наличие делаетвозможными, в частности, следующие описания:Complex z3; / / используем конструктор по умолчаниюComplex v [5 0 ]; / / конструктор будет вызван 50 раз/ / т .
е . для каждого элемента§2.5. К о н стр у к то р ы п р ео б р азован и яПри создании программ на языках, обладающих типизацией, нередко возникает потребность использовать значение одного типа там, гдепо смыслу предполагается значение другого типа. Один из простейшихпримеров такой ситуации — сложение (или другая арифметическая операция) числа с плавающей точкой и целого числа. В языке Си в подобных случаях производится так называемое неявное преобразованиетипов.
Так, если мы опишем переменную а как целочисленную, а переменную b как имеющую тип flo a t , и после этого используем в программевыражение а + Ь, компилятор неявно преобразует значение переменнойа к типу f lo a t , и только после этого выполнит сложение. Точно так же,если в программе описана функция void f ( f lo a t ) , мы можем записатьвызов f (25), и компилятор сочтет такой код корректным, т. к. «знает»,каким образом из целого числа сделать число с плавающей точкой; иначеговоря, в языке Си присутствует правило преобразования значений типаin t в значения типа flo a t.Естественно, язык С и + + также позволяет производить подобныенеявные преобразования, но, помимо этого, в нём имеются средства указания правил преобразования для типов данных, введенных пользова25т е л е м . Одним из таких средств являются к о н с т р у к т о р ы преобразования.Отвлекшись на время от конкретики языка СиН—Ь, заметим, что задать правило преобразования значений типа А в значения типа В — этото же самое, что задать инструкцию по созданию объекта типа В по имеющемуся значению типа А.
В самом деле, в разобранном выше примерекомпилятор сначала строил значение типа f lo a t , используя в качествеотправной точки имеющееся значение типа in t; разумеется, компилятору для этого необходимо «знать», как это делать.Как отмечалось в § 2.1.3, инструкции компилятору С и + + по созданию объектов класса на основе заданных параметров даются путем описания конструкторов с соответствующими параметрами. Таким образом,если ввести в классе В конструктор, получающий параметр типа А, этокак раз и будет инструкция по созданию объекта типа В по имеющемусязначению типа А. Представляется поэтому вполне логичным использовать такие инструкции для выполнения неявного преобразования т и п о в .Итак, конструктор, который получает на вход ровно один параметр,имеющий тип, отличный от описываемого, называется к о н с т р у к т о р о мпреобразования4 и используется компилятором не только в случае явного создания объекта, но и для проведения неявного преобразованиятипов.Проиллюстрируем сказанное на примере введенного ранее классаComplex.
Ясно, что по смыслу комплексных чисел любое действительное число может быть преобразовано к комплексному добавлением нулевой мнимой части точно так же, как целое число преобразуется к числу сплавающей точкой добавлением нулевой дробной части. Чтобы выразитьэто соотношение между действительными и комплексными числами, дополним класс следующим конструктором:Complex(double а) { re = a ; im = 0; }С одной стороны, этот конструктор позволяет нам описывать комплексные числа с указанием одного действительного параметра, например:Complex с (9 .7 );С другой стороны, если в программе имеется функцияvoid f(Complex а ) ;мы можем вызвать её для действительного параметра:f (2.7);4Если только специальное значение такого конструктора не отменить директивойe x p lic it.26Благодаря наличию в классе Complex конструктора преобразования компилятор сочтет такой вызов корректным; для его обработки с помощьюконструктора преобразования будет создан временный объект, типаComplex, который и будет подан на вход функции f .2.6.
С сы лкиПонятие ссылки является ключевым для понимания дальнейшегоматериала. Отметим, что в языке Си ничего подобного нет, так что ссылки вызывают определенные трудности у многих студентов. Постарайтесьпоэтому подойти к изучению данного параграфа особенно внимательно,а при возникновении вопросов обязательно задайте их вашему лекторуили преподавателю.Ссылка в С и + + — это особый вид объектов данных, реализуемыйпутем хранения адреса объекта, но семантически эквивалентный самомуобъекту, на который ссылается.
Иначе говоря, любые операции над ссылкой будут на самом деле производится над той переменной, на которуюэта ссылка установлена. Синтаксически тип данных «ссылка» описывается аналогично указателю, только вместо символа « *» используется символ «&»5.Проиллюстрируем сказанное простым примером:in t i ;in t *рin t &r/ / целочисленная переменная&i; / / указатель на переменную ii ; / / ссылка на переменную ii++;(*р )+ + ;г++;/ / увеличить i на 1/ / то же самое через указатель/ / то же самое по ссылкеСтрого говоря, ссылки нельзя называть переменными, т. к. изменить значение самой ссылки нельзя: если ссылка встречается в левой части операции присваивания — это означает, что присваивание будет выполненоне для ссылки как таковой, а для той переменной, на которую она ссылается. Таким образом, описав ссылку на переменную i, мы на самом делеввели синоним имени i — имя г, обозначающее тот же самый объектданных.Такое использование ссылок может показаться бессмысленным (хотяв некоторых случаях оказывается полезно).
Однако существенно более5Важно понимать, что в данном случае символ «&» не имеет ничего общего с операцией взятия адреса! Почему автор языка СиН—Ь Б. Страуструп выбрал именно такоеобозначение — вопрос открытый.27интересные возможности ссылочный тип в С и + + раскрывает при передаче его как параметра в функции и возврате из функций в качествезначения. Так, благодаря ссылочному типу в нашем распоряжении оказывается отсутствовавший в языке Си механизм передачи параметровпо ссылке6.
Допустим, нам нужно написать функцию, находящую максимальный и минимальный элементы заданного массива чисел типа float.На языке Си такая функция выглядела бы так:void max_min(float * a r r , in t le n , f lo a t *min, f lo a t *max){in t i ;*min = a r r [0 ];*max = a r r [0 ];f o r ( i = l ; i< le n ; i++) {if (*m in > a r r [i] ) *min = a r r [ i ] ;if(*m a x < a rr[i] ) *max = a r r [ i ] ;}}а её вызов — например, так:f lo a t а [5 0 0 ];f lo a t min, max;// ...max_min(a, 500, t a in , t a a x ) ;Поскольку из функции необходимо вернуть больше одного значения, приходится использовать возврат через параметры.
Между тем, в языке Сивсе параметры передаются по значению, поэтому нам приходится вручную имитировать выходные параметры, передавая значение адреса переменной, подлежащей модификации; поэтому при вызове функции мывынуждены применять операцию взятия адреса («&»). В самой функциивместо имени переменной мы вынуждены использовать леводопустимоевыражение разыменования, т. е. ставить перед идентификаторами min иmax символ операции разыменования « *» , чтобы преобразовать адрес переменной в саму переменную. С использованием ссылок и код функции,и её вызов обретают более ясный вид:void max_min(float * a r r , in t le n , f lo a t t a in , f lo a t taax){in t i ;min = a rr [0] ;max = a rr [0] ;®Читатель, скорее всего, уже знаком с передачей параметров по ссылке: в языкеПаскаль такие параметры называются параметрами-переменными (var-parameters).28f o r ( i = l ; i< le n ; i++) {if(m in > a r r [i]) min = a r r [ i ] ;if(m a x < a rr [i]) max = a r r [ i ] ;}}U ...f lo a t min, max;max_min(a, 500, min, max);В самом деле, параметры min и max, объявленные на сей раз как ссылки, семантически представляют собой синонимы неких целочисленныхпеременных, так что все операции, которые производятся над min и max,на самом деле происходят над теми переменными, на которые эти параметры ссылаются.
При вызове функции мы не применяем никакихопераций взятия адреса, поскольку (с семантической точки зрения) намдля построения синонима нужна сама переменная, а не её адрес.Довольно интересные возможности открывает использование ссылочного типа как типа возвращаемого значения функции. Допустим, необходимо произвести поиск целочисленной переменной в составе сложной структуры данных (например, искомая переменная является полемструктуры, которая, в свою очередь, является элементом массива и т.