Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Итог_Пособие C++.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.03 Mб
Скачать

1.13 Рекурсивные функции

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

В качестве простого примера дадим рекурсивное определение суммы первых n натуральных чисел. Сумма первых n натуральных чисел равна сумме первых (n – 1) натуральных чисел плюс n, а сумма первого числа равна 1. Или: Sn = Sn-1 + n; S1 = 1.

Функция, которая вычисляет сумму, пользуясь данным определением:

int sum(int n){

if (n==1) //условие выхода из рекурсии

sum=1;// терминальная ветвь

else

sum=sum(n-1)+n; // рекурсивная ветвь

};

Обратим внимание, что рекурсия как способ реализации алгоритма является альтернативой итерации (циклу). Так, приведенный пример с суммой легко реализуется при помощи цикла. При этом решение без рекурсии в большинстве случаев эффективнее рекурсивного решения, так что предыдущий пример – это только пример.

Но в некоторых случаев использование рекурсивной формы организации алгоритма позволяет реализовать компактный, наглядный программный код, работающий с приемлемой эффективностью. Классический пример – быстрая сортировка массивов Хоара, которая обычно реализуется с помощью рекурсивной функции.

Мы в качестве более интересного примера применения рекурсии рассмотрим следующую задачу. Требуется возвести число a в степень b (a и b – натуральные). Если выполнять это "в лоб", то нам потребуется выполнить b умножений: ab=a·a·a…·a (b раз). Однако, можно вычислять ab, используя следующие соображения:

Например, - итого всего 4 операции умножения, а не 7.

Несложно написать рекурсивную функцию, выполняющую вычисления по данной формуле:

unsigned int pow(unsigned int a, unsigned int b)

{

if (b==1) return a; //терминальная ветвь

else if (b % 2 == 0)

{

unsigned int p = pow(a,b/2);

return p*p;

}

else return pow(a,b-1)*a;

}

Конечно, для решения этой задачи вполне подходит и стандартная функция pow, но представленный вариант для целочисленных аргументов, возможно, будет эффективнее.

1.14 Пространства имён

Понятие пространства имён. Пространство имен — это декларативная область, в рамках которой определяются различные идентификаторы (имена типов, функций, переменных, и т. д.). Пространства имен используются для организации кода в виде логических групп и с целью избежания конфликтов имен, которые могут возникнуть, особенно в таких случаях, когда проект организован в виде нескольких файлов, разработанных разными программистами, или включает сторонние библиотеки.

Все идентификаторы в пределах пространства имен доступны друг другу без уточнения. За пределами пространства имен должно использоваться полное имя идентификатора, включающее имя пространства имён и имя самого идентификатора, например:

std::cout<<”Hello”<<std::endl;// endl тоже требует уточнения

 std::vector<std::string> vec; // вектор с элементами типа string

Инструкция using. Можно обойтись без уточнений имён, если использовать инструкцию using. Её можно поместить в верхнюю часть CPP-файла (в области видимости файла – это обычная практика) или внутрь определения класса или функции (в области видимости этого класса или функции). Имеется два варианта инструкции using:

для отдельного идентификатора, например:

using std::string;// далее можно использовать просто string без std

или  для всех идентификаторов в пространстве имен, например:

using namespace std;

Последнюю инструкцию мы уже неоднократно использовали в примерах пособия, при этом предупреждали, что это не самое лучшее решение. Действительно, в пространстве имён std объявлено огромное количество идентификаторов, поэтому нельзя исключить ситуацию, когда имя какого либо нашего объекта совпадёт с идентификатором, объявленным в std (возникнет конфликт имён). В таком случае совпадающий идентификатор из std окажется "закрытым" нашим объектом, что может впоследствии привести к трудно находимым ошибкам. Если используется всего несколько идентификаторов из std, то лучше каждый из них указать в отдельной инструкции using или в тексте программы использовать для них полные имена с указанием пространства имён.

Обратим внимание, что код в заголовочных файлах (с расширением .h) должен содержать полные имена всех идентификаторов – инструкция using в них является источником таких конфликтов имён, которые и предсказать зачастую невозможно.

Объявление пространства имён. Глобальное пространство имён и пространство имён std. Если в программе не объявляется никаких пространств имён, то считается, что всё описанные в ней объекты принадлежат глобальному пространству имён. Функция main всегда принадлежит глобальному пространству имён. Определения других функций программы рекомендуется размещать в пространствах имён, объявленных в этой программе (исключение можно сделать только для совсем небольших программ, таких как примеры данного пособия).

Объявить пространство имён можно так:

тamespace имя_пространства_имён

{программный код, содержащий объявления различных объектов

}

Имена всех объектов, описания которых содержатся внутри фигурных скобок, принадлежат объявленному пространству имён. Приведем простой пример.

// Пример 1.22 демонстрация использования пространств имён

#include <iostream>

namespace ns1{

int a=5;

void f(){std::cout<<a<<std::endl;}

}

namespace ns2{

int a=ns1::a+1;

void f(){std::cout<<a+1<<std::endl;}

}

int main(){

ns1::f(); ns2::f();

system ("pause"); return 0;

}

Пространство имен std.

Все типы и функции стандартной библиотеки C++ объявлены в пространстве имен std или в пространстве имен, вложенном в std.

Вложенные пространства имен. Пространство имен является вложенным, если оно размещено внутри другого (родительского) пространства имён. Обычное вложенное пространство имен имеет неограниченный доступ к объектам своего родительского пространства, но из родительского пространства нет неограниченного доступа к вложенному пространству имен (если оно не объявлено как встроенное), Обычные вложенные пространства имен можно использовать для сокрытия каких-либо деталей внутренней реализации, которые не нужны при использовании объектов родительского пространства имен.

Встроенные пространства имен (C++ 11). Встроенные пространства имён объявляются с использованием ключевого слова inline (inline namespace). В отличие от обычных вложенных пространств имен объекты встроенного пространства имен обрабатываются как и объекты родительского пространства имен. Эта особенность позволяет, например, выполнять поиск перегруженных функций, если имеются перегрузки в родительском и вложенном встроенном пространстве имен.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]