Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Aliens Vs Predator |#10| Human company final
Aliens Vs Predator |#9| Unidentified xenomorph
Aliens Vs Predator |#8| Tequila Rescue
Aliens Vs Predator |#7| Fighting vs Predator

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Страустрап Б. Весь текст 579.17 Kb

Язык С++

Предыдущая страница Следующая страница
1 ... 16 17 18 19 20 21 22  23 24 25 26 27 28 29 ... 50
  };

  void compute2(vec v);

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

                             - стр 126 -

  char* day[] = {
      "mon", "tue", "wed", "thu", "fri", "sat", "sun"
  };

  С  другой   стороны,  рассмотрим   определение  функции,  которая
работает с  двумерными  матрицами.  Если  размерность  известна  на
стадии компиляции, то никаких проблем нет:

  void print_m34(int m[3][4])
  {
      for (int i = 0; i<3; i++) {
          for (int j = 0; j<4; j++)
              cout << " " << m[i][j];
          cout << "\n";
      }
  }

  Матрица,  конечно,   все  равно  передается    как  указатель,  а
размерности используются просто для удобства записи.
  Первая размерность  массива не имеет отношения к задаче отыскания
положения  элемента  (#2.3.6).  Поэтому  ее  можно  передавать  как
параметр:

  void print_mi4(int m[][4], int dim1)
  {
      for (int i = 0; i
4.6.6 Параметры по Умолчанию

  Часто в  самом общем  случае функции требуется больше параметров,
чем в  самом простом  и более  употребительном случае.  Например, в
библиотеке  потоков   есть  функция  hex(),  порождающая  строку  с
шестнадцатиричным   представлением    целого.    Второй    параметр
используется для  задания числа  символов для представления первого
параметра. Если  число  символов  слишком  мало  для  представления
целого, происходит  усечение, если  оно слишком  велико, то  строка
дополняется пробелами.  Часто  программист  не  заботится  о  числе
символов, необходимых  для представления целого, поскольку символов
достаточно.  Поэтому   для  нуля   в  качестве   второго  параметра
определено значение "использовать столько символов, сколько нужно".
Чтобы избежать засорения программы вызовами вроде hex(i,0), функция
описывается так:

  extern char* hex(long, int =0);

Инициализатор второго  параметра является  параметром по умолчанию.
То есть, если в вызове дан только один параметр, в качестве второго
используется параметр по умолчанию. Например:

  cout << "**" << hex(31) << hex(32,3) << "**";

интерпретируется как

  cout << "**" << hex(31,0) << hex(32,3) << "**";

и напечатает:

  ** 1f 20**

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

                             - стр 128 -

  int f(int, int =0, char* =0);    // ok
  int g(int =0, int =0, char*);    // ошибка
  int f(int =0, int, char* =0);    // ошибка

Заметьте, что  в  этом  контексте  пробел  между  *  и  =  является
существенным (*= является операцией присваивания):

  int nasty(char*=0);                // синтаксическая ошибка

     4.6.7 Перегрузка Имен Функций

  Как правило, давать разным функциям разные имена - мысль хорошая,
но  когда   некоторые  функции   выполняют  одинаковую  работу  над
объектами разных  типов, может  быть более удобно дать им одно и то
же имя.  Использование одного  имени  для  различных  действий  над
различными типами  называется перегрузкой  (overloading). Метод уже
используется для  основных  операций  C++:  у  сложения  существует
только одно  имя, +,  но его  можно применять для сложения значений
целых, плавающих  и указательных  типов. Эта идея легко расширяется
на  обработку   операций,  определенных   пользователем,  то  есть,
функций.  Чтобы   уберечь  программиста  от  случайного  повторного
использования имени,  имя может  использоваться более чем для одной
функции только если оно сперва описано как перегруженное. Например:

  overload print;
  void print(int);
  void print(char*);

  Что касается компилятора, единственное общее, что имеют функции с
одинаковым именем, это имя. Предположительно, они в каком-то смысле
похожи, но  в этом  язык ни стесняет программиста, ни помогает ему.
Таким образом,  перегруженные имена  функций -  это главным образом
удобство записи.  Это  удобство  значительно  в  случае  функций  с
общепринятыми  именами   вроде  sqrt,   print  и  open.  Когда  имя
семантически значимо,  как это имеет место  для операций вроде +, *
и <<  (#6.2) и  в  случае  конструкторов  (#5.2.4  и  #6.3.1),  это
удобство становится  существенным. Когда  вызывается  перегруженная
f(), компилятор  должен понять,  к какой  из  функций  с  именем  f
следует обратиться.  Это делается путем сравнения типов фактических
параметров с  типами формальных параметров всех функций с именем f.
Поиск  функции,   которую  надо   вызвать,  осуществляется  за  три
отдельных шага:
  [1] Искать функцию соответствующую точно, и использовать ее, если
     она найдена;
  [2]   Искать   соответствующую   функцию   используя   встроенные
     преобразования и использовать любую найденную функцию; и
  [3]  Искать  соответствующую  функцию  используя  преобразования,
     оперделенные  пользователем   (#6.3),     и   если   множество
     преобразований единственно, использовать найденную функцию.
  Например:

                             - стр 129 -

  overload print(double), print(int);

  void f();
  {
      print(1);
      print(1.0);
  }

  Правило точного  соответствия гарантирует, что f напечатает 1 как
целое и  1.0 как  число с  плавающей точкой.  Ноль, char  или short
точно  соответствуют   параметру  int.   Аналогично,  float   точно
соответствует double.
  К параметрам  функций с  перегруженными именами  стандартные  C++
правила  преобразования   (#с.6.6)   применяются   не   польностью.
Преобразования,  могущие  уничтожить  информацию,  не  выполняются.
Остаются int  в long,  int в  double, ноль  в long, ноль в double и
преобразования указателей:  ноль  в  указатель,  ноль  в  void*,  и
указатель  на  производный  класс  в  указатель  на  базовый  класс
(#7.2.4).
  Вот пример, в котором преобразование необходимо:

  overload print(double), print(long);

  void f(int a);
  {
      print(a);
  }

  Здесь a  может быть  напечатано или  как double,  или  как  long.
Неоднозначность  разрешается   явным  преобразованием   типа   (или
print(long(a)) или print(double(a))).
  При этих  правилах можно  гарантировать, что  когда эффективность
или  точность   вычислений  для   используемых  типов   существенно
различаются, будет  использоваться простейший  алгоритм  (функция).
Например:

  overload pow;
  int pow(int, int);
  double pow(double, double);      // из
  complex pow(double, complex);    // из
  complex pow(complex, int);
  complex pow(complex, double);
  complex pow(complex, complex);

  Процесс поиска подходящей функции игнорирует unsigned и const.

     4.6.8 Незаданное Число Параметров

  Для  некоторых   функций  невозможно  задать  число  и  тип  всех
параметров, которые можно ожидать в вызове. Такую функцию описывают
завершая список описаний параметров многоточием (...), что означает
"и может быть, еще какие-то параметры". Например:

  int printf(char* ...);

                             - стр 130 -

  Это задает,  что в вызове printf должен быть по меньшей мере один
параметр, char*,  а остальные  могут  быть,  а  могут  и  не  быть.
Например:

  printf("Hello, world\n");
  printf("Мое имя %s %s\n", first_name, second_name);
  printf("%d + %d = %d\n",2,3,5);

  Такая  функция   полагается  на  информацию,  которая  недоступна
компилятору  при  интерпретации  ее  списка  параметров.  В  случае
printf() первым  параметром  является  строка  формата,  содержащая
специальные  последовательности   символов,  позволяющие   printf()
правильно  обрабатывать   остальные  параметры.  %s  означает  "жди
параметра char*",  а  %d  означает  "жди  параметра  int".  Однако,
компилятор этого не знает, поэтому он не может убедиться в том, что
ожидаемые параметры имеют соответствующий тип. Например:

  printf("Мое имя %s %s\n",2);

откомпилируется и в лучшем случае приведет к какой-нибудь странного
вида выдаче.
  Очевидно, если  параметр не  был описан,  то  у  компилятора  нет
информации, необходимой  для выполнения  над ним  проверки  типа  и
преобразования типа.  В этом  случае char  или short передаются как
int, а  float передается  как double.  Это не  обязательно то, чего
ждет пользователь.
  Чрезмерное использование  многоточий, вроде  wild(...), полностью
выключает проверку типов параметров, оставляя программиста открытым
перед   множеством    неприятностей,   которые    хорошо    знакомы
программистам на  C. В хорошо продуманной программе требуется самое
большее  несколько   функций,  для   которых  типы   параметров  не
определены полностью.  Для  того,  чтобы  позаботиться  о  проверке
типов,  можно   использовать  перегруженные  функции  и  функции  с
параметрами по  умолчанию в  большинстве тех  случаев, когда  иначе
пришлось  бы   оставить  типы  параметров  незаданными.  Многоточие
необходимо  только  если  изменяются  и  число  параметров,  и  тип
параметров.  Наиболее   обычное  применеие   многоточия  в  задании
интерфейса с  функциями C  библиотек, которые  были определены в то
время, когда альтернативы не было:

  extern int fprintf(FILE*, char* ...);    // из
  extern int execl(char* ...);             // из
  extern int  abort(...);                  // из

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

                             - стр 131 -

  void error(int ...);

  main(int argc, char* argv[])
  {
      switch(argc) {
      case 1:
          error(0,argv[0],0);
          break;
      case 2:
          error(0,argv[0],argv[1],0);
      default:
          error(1,argv[0],"с",dec(argc-1),"параметрами",0);
      }
  }

  Функцию ошибок можно определить так:

  #include

  void error(int n ...)
  /*
      "n" с последующим списком char*, оканчивающихся нулем
  */
  {
      va_list ap;
      va_start(ap,n);            // раскрутка arg

      for (;;) {
      char* p = va_arg(ap,char*);
      if(p == 0) break;
      cerr << p << " ";
      }

      va_end(ap);                // очистка arg

      cerr << "\n";
      if (n) exit(n);
  }

  Первый  из   va_list  определяется   и  инициализируется  вызовом
va_start(). Макрос va_start получает имя va_list'а и имя последнего
формального параметра как параметры. Макрос va_arg используется для
выбора неименованных  параметров по  порядку. При  каждом обращении
программист должен  задать  тип;  va_arg()  предполагает,  что  был
передан фактический  параметр, но  обычно способа  убедиться в этом
нет.  Перед   возвратом  из  функции,  в  которой  был  использован
va_start(),  должен  быть  вызван  va_end().  Причина  в  том,  что
va_start() может  изменить  стек  так,  что  нельзя  будет  успешно
осуществить возврат; va_end() аннулирует все эти изменения.

     4.6.9 Указатель на Функцию

  С функцией  можно делать  только две вещи: вызывать ее и брать ее
адрес. Указатель,  полученный взятием  адреса функции,  можно затем
использовать для вызова этой функции. Например:

                             - стр 132 -

  void error(char* p) { /* ... */ }

  void (*efct)(char*);                // указатель на функцию

  void f()
  {
      efct = &error;                  // efct указывает на error
      (*efct)("error");               // вызов error через efct
  }

Чтобы вызвать функцию через указатель, например, efct, надо сначала
этот  указатель  разыменовать,  *efct.  Поскольку  операция  вызова
Предыдущая страница Следующая страница
1 ... 16 17 18 19 20 21 22  23 24 25 26 27 28 29 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама