И.А. Волкова, А.В. Иванов, Л.Е. Карпов - Основы объектно-ориентированного программирования. Язык программирования С++ (1114893), страница 9
Текст из файла (страница 9)
Отбираются функции с необходимым количеством формальныхпараметров.55Статический полиморфизмПримечаниеВозможно описание функции с переменным числом параметров. Для этогоиспользуются символы «…» в конце списка формальных параметров, обозначающих произвольное количество дополнительных неименованных параметров, типы которых будут определяться непосредственно при вызове функции.Пример прототипа функции с переменным числом параметров:void f1 (int i1, . . .);В этом случае внутри функции необходимо иметь специальные средства получения значений таких дополнительных параметров, не имеющих собственных имен.
При обработке списка формальных параметров компилятор неимеет информации, необходимой для выполнения стандартной проверкии преобразования типов неименованных параметров. Поэтому средства получения таких параметров могут использовать только информацию, недоступную компилятору. Для облегчения работы с этими параметрами в файлеstdarg.h стандартной библиотеки имеются описания структурыva_listи функций: va_start(), va_arg(), va_end().
Описания, содержащиесяв библиотечном файле ‹cstdarg›, становятся доступными после его подключения директивой препроцессора #include ‹cstdarg›.Функции с переменным числом параметров рекомендуется использоватьв исключительных случаях, когда типы параметров действительно неизвестны. в большинстве случаев можно использовать функции с аргументами поумолчанию или функцией с двумя параметрами следующего вида: первыйпараметр — целое число, равное количеству содержательных параметров,второй аргумент — указатель на массив указателей на фактические параметры.
Такой метод используется при передаче списка строковых параметров изкомандной строки вызова программы на исполнение:int main ( int argc, char* argv[] )Здесь args — количество строковых параметров в командной строке вызовапрограммы на исполнение, включая идентификатор программы.2. Для каждого фактического параметра вызова функции строитсямножество функций, оптимально отождествляемых по этому параметру (best matching).3. Находится пересечение этих множеств.4.
Если полученное множество состоит из одной функции, то вызовразрешим. Если множество пусто или содержит более одной функции, то генерируется сообщение об ошибке.Пример:class x{. . .public:x ( int i1 ) {. . . }. . .};class y {. . .};void f ( x x1, int i1 ){. .
.}void f ( x x1, double d1 ) {. . .}void f ( y y1, double d1 ) {. . .}56Статический полиморфизмvoid g() {. . . f (1, 1) . . .} // вызов первой реализацииf(x, int)Пример:class x{. . .public:x(int i1){. . . }. . .};void f (x x1, int i1 ) {. . .}void f (int i1, x x1 ) {. . .}void g () {. . . f(1, 1) . . .}// ошибка: пересечение// множеств — пусто.8.4. Алгоритм поиска оптимальноотождествляемой функциидля одного параметраЕсли функция имеет один параметр, то выполняется следующая последовательность шагов для поиска оптимально отождествляемой функции.Такая же последовательность шагов выполняется для каждого параметра функции с несколькими параметрами на втором этапе ранее описанного алгоритма поиска оптимально отождествляемой функциис несколькими параметрами.1. Точное отождествление.2.
Отождествление с помощью расширений.3. Отождествление с помощью стандартных преобразований.4. Отождествление с помощью преобразований пользователя.5. Отождествление по «…».Данные шаги определяют приоритет метода отождествления параметра.в рамках каждого шага разные виды преобразований являются равно приоритетными.8.4.1 Точное отождествлениеРазличают следующие виды точного отождествления:— Точное совпадение— Совпадение с точностью до typedef— Тривиальные преобразования: T[] ↔ T*57Статический полиморфизмT ↔ T&T → const TПример:voidvoidvoidvoidfffg(float);(double);(int);() { . .
. f(1.0F); . . . f(1.0); . . . f(1);}8.4.2 Отождествление с помощьюрасширений8.4.2.1. Целочисленные расширенияРазличают следующие виды целочисленного расширения:ПримечаниеЕсли фактический параметр невозможно преобразовать к типу signed int безпотери информации, то осуществляется его преобразование к типу unsignedint. Дело в том, что на три основных целочисленных типа — 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 — невозможно.8.4.2.2. Расширения с плавающей точкойРасширение с плавающей точкой допускает следующее преобразование:float → double8.4.3 Отождествление с помощьюстандартных преобразований8.4.3.1.
Остальные стандартные целочисленныеи вещественные преобразованияПример:#include <iostream>58Статический полиморфизм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';}int main(){f (-1);return 0;}ПримечаниеПри этом тип char — целочисленное значение размером в 1 байт.В выходной поток будет выдано:255 ////-1 ////8-битное представление числа -1,преобразованное в беззнаковое целоерасширенный до целого дополнительный код,представляющий -18.4.3.2. Преобразование указателейКонстанта 0 при преобразовании параметров рассматривается не только какчисло, но и как нулевой указатель. Нулевой указатель может быть преобразован в любой указатель. Любой указатель может быть преобразован в такназываемый обобщенный (свободный) указатель void*. При этом обратноепреобразование должно выполняться явно.Пример:int main(){int i;void* p;int *ip = &i;////////int* ip2;//*ip = 15;////p = ip;////ip2 = (int*)p; //Обобщенный указательУказатель на int,инициализируемый адресомцелочисленной переменнойУказатель на intРазыменование указателя наintДопустимое неявное преобразованиеуказателяЯвно заданное преобразование59Статический полиморфизм// указателя...return 0;}В связи с тем, что тип char*, используемый для работы с динамическиразмещаемыми строками, является указателем, то при наличии перегруженных функций с прототипами:int f ( char* c1 );int f ( double d1 );вызов i = f (0); будет неоднозначным, так как при отождествлениис помощью стандартных преобразований следующие преобразования равноправны:0 -> double (int -> double)0 -> char* (пустой указатель -> указатель на char)Указатель на объект производного класса может быть преобразованв указатель на объект базового класса.
О наследовании классов будет подробно рассказано далее.Пример: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Через указатель на объект базового класса доступны только члены производного класса, унаследованные от базового класса. Это связано с тем, что,как было отмечено, указатели являются типизированными, то есть, в указателехранится не только адрес объекта, но и информация о структуре объекта.60Статический полиморфизмПримечание 2Как будет описано далее, неявное преобразование указателей возможнотолько при открытом (public) — наследовании.8.4.4 Преобразования пользователя8.4.4.1.
Конструктор преобразованияКонструктор преобразования может быть использован в качестве неявногопреобразования пользователя только в том случае, если он объявлен безключевого слова explicit.8.4.4.2. Функция преобразования (операция преобразования)Функция преобразования — это метод класса с прототипом:operator тип();Здесь тип — тип возвращаемого значения (встроенного или пользовательского).Тело данной функции определяет возвращаемое значение.
Тело функции должно содержать оператор return, возвращающее данное значение. Приэтом возвращаемое значение преобразуется к указанному типу. Если типявляется пользовательским, то создается временный объект данного типа.При этом вызывается конструктор преобразования, определенный в этомклассе. Возвращаемое функцией значение будет входным параметром дляэтого конструктора.Функция преобразования выполняет действие, обратное действиюконструктора преобразования: если конструктор преобразования создаетобъект типа описываемого класса на основе объекта другого типа, то функция преобразования возвращает объект другого типа на основе объекта(внутренней информации, хранящейся в объекте) описываемого класса.Сходством функции преобразования и конструктора преобразованияявляется то, что у функции преобразования так же, как и у конструкторапреобразования нет типа возвращаемого значения.
Тип возвращаемого значения у функции преобразования определяется типом, указанным послеключевого слова operator.Кроме функции преобразования ключевое слово operator используетсятакже при перегрузке операций, о чем уже было рассказано.При использовании преобразования пользователя строятся все возможные цепочки преобразований параметра, которые позволяют применитьодну из реализаций перегруженной функции. При этом выбирается та реализация, для которой в цепочке преобразований шаг преобразованияс минимальным приоритетом имеет максимальный приоритет по сравнениюс другими цепочками. Так, если в одной цепочке преобразований шагомс минимальным приоритетом будет шаг 4 (преобразование пользователя),а в другой цепочке — шаг 3 (стандартные преобразования), то будет выбрана61Статический полиморфизмреализация функции, для которой цепочка преобразований параметра содержит шаг 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';}void 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():1.