Пространства имен стандартной библиотеки
Объекты стандартной библиотеки определены в пространстве имен std. Например, объявления стандартных средств ввода/вывода С в заголовочном файле <stdio.h> помещены в пространство имен следующим образом:
// stdio.h
namespace std{
int printf(const char*...);
... }
using namespace std;
Это обеспечивает совместимость сверху вниз. Для тех, кто не желает присутствия неявно доступных имен, определен новый заголовочный файл <cstdio>:
// cstdio
namespace std{
int printf(const char*...);
... }
Если в программу включен файл <cstdio>, нужно указывать имя пространства имен явным образом:
std::printf("...");
Механизм пространств имен вместе с директивой #include обеспечивают необходимую при написании больших программ гибкость путем сочетания логического группирования связанных величин и ограничения доступа к ненужным средствам.
Перегрузка функций
Функции в С++ могут перегружаться. Это означает, что несколько функций могут иметь одинаковые имена, но различную реализацию. Одинаковые имена позволяют задавать одинаковые по смыслу действия - такие как показать, стереть, найти, напечатать и др. Кроме различной реализации, перегружаемые функции должны отличаться друг от друга либо типами формальных параметров, либо их количеством, либо и тем и другим.
Примеры:
1) void print ( int ); // печать целых
2) void print ( const char* ); // печать строк
3) void print ( double ); // печать вещественных
4) void print ( char ); // печать символов
При вызове таких функций компилятор сам различает, какую из них вызывать, например:
int i; char c; float f;
............................
print ( c ); // вызывается ф-ция ( 4 ).
print ( i ); // вызывается ф-ция ( 1 ).
print ( f ); // вызывается ф-ция ( 3 ), при этом float преобразуется в // double.
Если для функции задан необязательный аргумент, то фактически задано несколько подписей этой функции. Например, попытка определения двух функций
double expnt (double x, unsigned int e = 2);
double expnt (double x);
приведет к ошибке компиляции – неоднозначности определения функции. Это происходит потому, что вызов
double x = expnt(4.1);
подходит как для первой, так и для второй функции.
Отметим, что тип возвращаемого значения не может использоваться для различения перегружаемых функций.
Рекурсия
Определения функций не могут быть вложенными, т.е. нельзя внутри тела одной функции определить тело другой. Разумеется, можно вызвать одну функцию из другой. В том числе функция может вызвать сама себя.
Рассмотрим функцию вычисления факториала целого числа. Ее можно реализовать двумя способами.
|
Первый способ использует итерацию: int fact(int n) { int result = 1; for (int i = 1; i <= n; i++) result = result * i; return result; }
|
Второй способ: int fact(int n) { if (n == 1) // факториал 1 равен 1 return 1; else // факториал числа n равен // факториалу n-1 // умноженному на n
return n * fact(n -1);}
|
Функция fact вызывает сама себя с модифицированными аргументами. Такой способ вычислений называется рекурсией. Рекурсия (прямая, косвенная) – это очень мощный метод вычислений. Значительная часть математических функций определяется в рекурсивных терминах. В программировании алгоритмы обработки сложных структур данных также часто бывают рекурсивными. Рассмотрим, например, структуру двоичного дерева. Дерево состоит из узлов и направленных связей. С каждым узлом могут быть связаны один или два узла, называемые сыновьями этого узла. Соответственно, для "сыновей" узел, из которого к ним идут связи, называется "отцом". Узел, у которого нет "отца", называется корнем. У дерева есть только один корень. Узлы, у которых нет "сыновей", называются листьями. Пример дерева приведен на рис. 1.

Рис. 1. Пример дерева.
В этом дереве узел A – корень дерева, узлы B и C – "сыновья" узла A, узлы D и E – "сыновья" узла B, узел F – "сын" узла C. Узлы D, E и F – листья. Узел B является корнем поддерева, состоящего из трех узлов B, D и E. Обход дерева (прохождение по всем его узлам) можно описать таким образом:
-
Посетить корень дерева.
-
Обойти поддеревья с корнями — "сыновьями" данного узла, если у узла есть "сыновья".
-
Если у узла нет "сыновей" — обход закончен.
Очевидно, что реализация такого алгоритма с помощью рекурсии не составляет труда.
Довольно часто рекурсия и итерация взаимозаменяемы (как в примере с факториалом). Выбор между ними может быть обусловлен разными факторами. Чаще рекурсия более наглядна и легче реализуется. Однако, в большинстве случаев итерация более эффективна.
Директива связывания extern "C"
Если программист хочет использовать функцию, написанную на другом языке, в частности на С, то компилятору нужно указать, что при вызове требуются несколько иные условия. Скажем, имя функции или порядок передачи аргументов различаются в зависимости от языка программирования.
Показать, что функция написана на другом языке, можно с помощью директивы связывания в форме простой либо составной инструкции:
// директива связывания в форме простой инструкции
extern "C" void exit(int);
// директива связывания в форме составной инструкции
extern "C" {
int printf( const char* ... );
int scanf( const char* ... );
}// директива связывания в форме составной инструкции
extern "C" { #include <cmath> }
Первая форма такой директивы состоит из ключевого слова extern, за которым следует строковый литерал, а за ним – “обычное” объявление функции. Хотя функция написана на другом языке, проверка типов вызова выполняется полностью. Несколько объявлений функций могут быть помещены в фигурные скобки составной инструкции директивы связывания – второй формы этой директивы. Скобки отмечают те объявления, к которым она относится, не ограничивая их видимости, как в случае обычной составной инструкции. Составная инструкция extern "C" в предыдущем примере говорит только о том, что функции printf() и scanf() написаны на языке С. Во всех остальных отношениях эти объявления работают точно так же, как если бы они были расположены вне инструкции.
Если в фигурные скобки составной директивы связывания помещается директива препроцессора #include, все объявленные во включаемом заголовочном файле функции рассматриваются как написанные на языке, указанном в этой директиве. В предыдущем примере все функции из заголовочного файла cmath написаны на языке С.
Директива связывания не может появиться внутри тела функции.
