И.А. Волкова, А.В. Иванов, Л.Е. Карпов - Основы объектно-ориентированного программирования. Язык программирования С++ (ЧБ) (1114895), страница 10
Текст из файла (страница 10)
. f(1.0F); . . . f(1.0); . . . f(1);}492). Отождествление с помощью расширений2.1). Целочисленные расширения⎧ ch a r⎫unsignedshort⎪⎪⎨ sig n ed sh o rt ⎬ → in t⎪ beno uo ml⎪⎩⎭Примечание. Если фактический параметр невозможно преобразовать к типуsigned int без потери информации, то осуществляется его преобразование к типуunsigned int. Дело в том, что на три основных целочисленных типа – short, int иlong, в самых распространенных компиляторах отводится два основных размера – 2 и 4байта. При этом, в разных компиляторах одинаковый размер имеют разные целочисленные типы. Так, в компиляторе Borland С++ 3.1 типы short и int имеют размер 2байта, а long – 4 байта.
В компиляторах Borland С++ 5.02 и Microsoft Visual C++ 6.0 типshort имеет размер 2 байта, а типы int и long – 4 байта. Поэтому, если типы short иint имеют одинаковый размер, то преобразование unsigned short -> signed int –невозможно.2.2). Расширения с плавающей точкой.float -> double3). Отождествление с помощью стандартных преобразований3.1). Остальные стандартные целочисленные и вещественные преобразованияПримечание. При этом тип char - целочисленное значение размером в 1 байт:Пример:#include <iostream>using namespace std;void f(char c){int i;union U {char c1;unsigned b:8;};U u1;u1.c1 = c;cout << u1.b << '\n';i = c;cout << i << '\n';}50int main() {f(-1);return 0;}В выходной поток будет выдано:// 8-битное представление числа -1, преобразованное в беззнаковое целое255-1// расширенный до целого дополнительный код, представляющий -13.2).
Преобразование указателейКонстанта 0 при преобразовании параметров рассматривается не только какчисло, но и как нулевой указатель. Нулевой указатель может быть преобразован влюбой указатель. Любой указатель может быть преобразован в так называемыйобобщенный (свободный) указатель void*. При этом обратное преобразованиедолжно выполняться явно.Пример:int main() {int i;void* p;int *ip = &i;int* ip2;*ip = 15;p = ip;ip2 = (int*)p;// Обобщенный указатель// Указатель на int,// инициализируемый адресом// целочисленной переменной// Указатель на int// Разыменование указателя на// int// Допустимое неявное преобразование// указателя// Явно заданное преобразование// указателя.
. .return 0;}В связи с тем, что тип char*, используемый для работы с динамическиразмещаемыми строками, является указателем, то при наличии перегруженныхфункций с прототипами:int f(char* c1);int f(double d1);вызовi = f(0);будет неоднозначным, так как при отождествлении с помощью стандартных преобразований следующие преобразования:0 -> double (int -> double)0 -> char* (пустой указатель -> указатель на char)равноправны.51Указатель на объект производного класса может быть преобразован в указатель на объект базового класса. О наследовании классов будет подробно рассказано далее.Пример:class X {int x1;public:X():x1(0){}};class Y: public X {int y1;public:Y(): y1(0){}};void f(X * x2){}int main() {Y * y2;f(y2); // фактический параметр – указатель на объект// производного класса, а формальный параметр// – указатель на объект базового классаreturn 0;}Замечание 1. Через указатель на объект базового класса доступны толькочлены производного класса, унаследованные от базового класса.
Это связано стем, что, как было отмечено, указатели являются типизированными, то есть, вуказателе хранится не только адрес объекта, но и информация о структуре объекта.Замечание 2. Как будет описано далее, неявное преобразование указателейвозможно только при открытом (public) – наследовании.4). Преобразования пользователя4.1). Конструктор преобразованияКонструктор преобразования может быть использован в качестве неявногопреобразования пользователя только в том случае, если он объявлен без ключевого слова explicit4.2).
Функция преобразования (операция преобразования)Функция преобразования – это метод класса с прототипом:operator тип();Здесь тип – тип возвращаемого значения (встроенного или пользовательского).52Тело данной функции определяет возвращаемое значение. Тело функциидолжно содержать оператор return, возвращающее данное значение. При этомвозвращаемое значение преобразуется к указанному типу. Если тип являетсяпользовательским, то создается временный объект данного типа.
При этом вызывается конструктор преобразования, определенный в этом классе. Возвращаемое функцией значение будет входным параметром для этого конструктора.Функция преобразования выполняет действие, обратное действию конструктора преобразования: если конструктор преобразования создает объект типаописываемого класса на основе объекта другого типа, то функция преобразованиявозвращает объект другого типа на основе объекта (внутренней информации,хранящейся в объекте) описываемого класса.Сходством функции преобразования и конструктора преобразования является то, что у функции преобразования так же, как и у конструктора преобразования нет типа возвращаемого значения.
Тип возвращаемого значения у функциипреобразования определяется типом, указанным после ключевого слова operator.Кроме функции преобразования ключевое слово operator используетсятакже при перегрузке операций, о чем уже было рассказано.При использовании преобразования пользователя строятся все возможныецепочки преобразований параметра, которые позволяют применить одну из реализаций перегруженной функции. При этом выбирается та реализация, для которой в цепочке преобразований шаг преобразования с минимальным приоритетом имеет максимальный приоритет по сравнению с другими цепочками.
Так,если в одной цепочке преобразований шагом с минимальным приоритетом будетшаг 4 (преобразование пользователя), а в другой цепочке – шаг 3 (стандартныепреобразования), то будет выбрана реализация функции, для которой цепочкапреобразований параметра содержит шаг 3.Пример:#include <iostream>using namespace std;class S {long ss1;public:S():ss1(0){}S(long p):ss1(p){cout << "constructor S -> ";}operator int(){cout << "S.operator int() -> ";return ss1;}};void f(long p){cout << "f(long)" << '\n';}53void f(char* p){cout << "f(char*)" << '\n';}void g(S p){cout << "g(S)" << '\n';}void g(char* p){cout << "g(char*)" << '\n';}void ex(S& a) {f(a);g(1);g(0);}int main(){S s1;ex(s1);return 0;}Последовательности преобразований параметров при вызове функций f и g вфункции ex():f(a): s.operator int(): шаг 4.
Преобразование пользователяint -> long : шаг 3. Стандартное преобразованиеf(long)g(1): int -> long : шаг 2. Расширение (целочисленное)s.constructor(long): шаг 4. Преобразование пользователяg(s)g(0): 0 -> char* : шаг 3. Стандартные преобразования(преобразования указателей – константа 0преобразуется в указатель)g(char*)Замечание 1.
Пользовательские преобразования применимы неявно только втом случае, если они однозначны.Пример:class B {int i1;public:B(int p):i1(p){}operator int(){ return i1; }B operator+(const B & pb){54B tmp(0);tmp.i1 = i1 + pb.i1;return tmp;}};int main(){B x(0);x = x + 1; // неоднозначность: возможно// x.operator int()+1 либо// x.operator+(B(1))return 0;}Замечание 2. Допустимо не более одного пользовательского преобразования для одного параметра.Пример:class X {int x1;public:operator int(){return x1;}X(int px):x1(px){}};class Y {int y1;public:operator X(){X tmp(y1);return tmp;}Y():y1(0){}};int main(){Y a;int b;b = a;return 0;// ошибка: требуется// a.operator X().operator int()}5).
Отождествление по ‘...’Использование символов ‘...’ в конце списка формальных параметров вописании функции с переменным числом параметров для обозначения оставшихся неявных параметров было описано ранее.55Пример:#include <iostream>using namespace std;class X {double x1;public:X(double px):x1(px){}};void f(int i1, X x2){cout << "f(int, X)" << '\n';}void f(int i1, ...){cout << "f(int, ...)" << '\n';}int main(){f(1,1);f(1,"Test");return 0;}Использование символов ‘.