<< Пред. стр. 6 (из 121) След. >>
По умолчанию в программе видны объекты, объявленные без явного указания пространства имен; они относятся к глобальному пространству имен. Для того чтобы обратиться к объекту из другого пространства, нужно использовать его квалифицированное имя, которое состоит из идентификатора пространства имен и идентификатора объекта, разделенных оператором разрешения области видимости (::). Вот как выглядят обращения к объектам приведенных выше примеров:Cplusplus_Primer_3E::Array
IBM_Canada_Laboratory::Matrix mat;
Disney_Feature_Animation::Point origin(5000,5000);
Для удобства использования можно назначать псевдонимы пространствам имен. Псевдоним выбирают коротким и легким для запоминания. Например:
// псевдонимы
namespace LIB = IBM_Canada_Laboratory;
namespace DFA = Disney_Feature_Animation;
int main()
{
LIB::Array
}
Псевдонимы употребляются и для того, чтобы скрыть использование пространств имен. Заменив псевдоним, мы можем сменить набор задействованных функций и классов, причем во всем остальном код программы останется таким же. Исправив только одну строчку в приведенном выше примере, мы получим определение уже совсем другого массива:
namespace LIB = Cplusplus_Primer_3E;
int main()
{
LIB::Array
}
Конечно, чтобы это стало возможным, необходимо точное совпадение интерфейсов классов и функций, объявленных в этих пространствах имен. Представим, что класс Array из Disney_Feature_Animation не имеет конструктора с одним параметром – размером. Тогда следующий код вызовет ошибку:
namespace LIB = Disney_Feature_Animation;
int main()
{
LIB::Array
}
Еще более удобным является способ использования простого, неквалифицированного имени для обращения к объектам, определенным в некотором пространстве имен. Для этого существует директива using:
#include "IBM_Canada_Laboratory.h"
using namespace IBM_Canada_Laboratory;
int main()
{
// IBM_Canada_Laboratory::Matrix
Matrix mat(4,4);
// IBM_Canada_Laboratory::Array
Array
// ...
}
Пространство имен IBM_Canada_Laboratory становится видимым в программе. Можно сделать видимым не все пространство, а отдельные имена внутри него (селективная директива using):
#include "IBM_Canada_Laboratory.h"
using namespace IBM_Canada_Laboratory::Matrix;
// видимым становится только Matrix
int main()
{
// IBM_Canada_Laboratory::Matrix
Matrix mat(4,4);
// Ошибка: IBM_Canada_Laboratory::Array невидим
Array
// ...
}
Как мы уже упоминали, все компоненты стандартной библиотеки С++ объявлены внутри пространства имен std. Поэтому простого включения заголовочного файла недостаточно, чтобы напрямую пользоваться стандартными функциями и классами:
#include
// ошибка: string невидим
string current_chapter = "Обзор С++";
Необходимо использовать директиву using:
#include
using namespace std;
// Ok: видим string
string current_chapter = "Обзор С++";
Заметим, однако, что таким образом мы возвращаемся к проблеме “засорения” глобального пространства имен, ради решения которой и был создан механизм именованных пространств. Поэтому лучше использовать либо квалифицированное имя:
#include
// правильно: квалифицированное имя
std::string current_chapter = "Обзор С++";
либо селективную директиву using:
#include
using namespace std::string;
// Ok: string видим
string current_chapter = "Обзор С++";
Мы рекомендуем пользоваться последним способом.
В большинстве примеров этой книги директивы пространств имен были опущены. Это сделано ради сокращения размера кода, а также потому, что большинство примеров были скомпилированы компилятором, не поддерживающим пространства имен – достаточно недавнего нововведения С++. (Детали применения using-объявлений при работе с стандартной библиотекой С++ обсуждаются в разделе 8.6.)
В нижеследующих главах мы создадим еще четыре класса: String, Stack, List и модификацию Stack. Все они будут заключены в одно пространство имен – Cplusplus_Primer_3E. (Более подробно работа с пространствами имен рассматривается в главе 8.)
Упражнение 2.21
Дано пространство имен
namespace Exercize {
template
class Array { ... };
template
void print (Array< EType > );
class String { ... }
template
class List { ... };
}
и текст программы:
int main() {
const int size = 1024;
Array
List
// ...
Array
List
print (*pas);
}
Программа не компилируется, поскольку объявления используемых классов заключены в пространство имен Exercise. Модифицируйте код программы, используя
(a) квалифицированные имена
(b) селективную директиву using
(c) механизм псевдонимов
(d) директиву using
2.8. Стандартный массив – это вектор
Хотя встроенный массив формально и обеспечивает механизм контейнера, он, как мы видели выше, не поддерживает семантику абстракции контейнера. До принятия стандарта C++ для программирования на таком уровне мы должны были либо приобрести нужный класс, либо реализовать его самостоятельно. Теперь же класс массива является частью стандартной библиотеки C++. Только называется он не массив, а вектор.
Разумеется, вектор реализован в виде шаблона класса. Так, мы можем написать
vector
vector
Есть два существенных отличия нашей реализации шаблона класса Array от реализации шаблона класса vector. Первое отличие состоит в том, что вектор поддерживает как присваивание значений существующим элементам, так и вставку дополнительных элементов, то есть динамически растет во время выполнения, если программист решил воспользоваться этой его возможностью. Второе отличие более радикально и отражает существенное изменение парадигмы проектирования. Вместо того чтобы поддержать большой набор операций-членов, применимых к вектору, таких, как sort(), min(), max(), find()и так далее, класс vector предоставляет минимальный набор: операции сравнения на равенство и на меньше, size() и empty(). Более общие операции, перечисленные выше, определены как независимые обобщенные алгоритмы.
Для использования класса vector мы должны включить соответствующий заголовочный файл.
#include
// разные способы создания объектов типа vector
vector
const int size = 8;
const int value = 1024;
// вектор размером 8
// каждый элемент инициализируется 0
vector
// вектор размером 8
// каждый элемент инициализируется числом 1024
vector
// вектор размером 4
// инициализируется числами из массива ia
int ia[4] = { 0, 1, 1, 2 };
vector
// vec4 - копия vec2
vector
Так же, как наш класс Array, класс vector поддерживает операцию доступа по индексу. Вот пример перебора всех элементов вектора:
#include
extern int getSize();
void mumble()
{
int size = getSize();
vector
for (int ix=0; ix
// ...
}
Для такого перебора можно также использовать итераторную пару. Итератор – это объект класса, поддерживающего абстракцию указательного типа. В шаблоне класса vector определены две функции-члена – begin() и end(), устанавливающие итератор соответственно на первый элемент вектора и на элемент, который следует за последним. Вместе эти две функции задают диапазон элементов вектора. Используя итератор, предыдущий пример можно переписать таким образом:
#include
extern int getSize();
void mumble()
{
int size = getSize();
vector
vector
for (int ix=0; iter!=vec.end(); ++iter, ++ix)
*iter = ix;
// ...
}
Определение переменной iter
vector
инициализирует ее адресом первого элемента вектора vec. iterator определен с помощью typedef в шаблоне класса vector, содержащего элементы типа int. Операция инкремента
++iter
перемещает итератор на следующий элемент вектора. Чтобы получить сам элемент, нужно применить операцию разыменования:
*iter
В стандартной библиотеке С++ имеется поразительно много функций, работающих с классом vector, но определенных не как функции-члены класса, а как набор обобщенных алгоритмов. Вот их неполный перечень:
алгоритмы поиска: find(), find_if(), search(), binary_search(), count(), count_if();
алгоритмы сортировки и упорядочения: sort(), partial_sort(), merge(), partition(), rotate(), reverse(), random_shuffle();
алгоритмы удаления: unique(), remove();
численные алгоритмы: accumulate(), partial_sum(), inner_product(), adjacent_difference();
алгоритмы генерации и изменения последовательности: generate(), fill(), transform(), copy(), for_each();
алгоритмы сравнения: equal(), min(), max().
В число параметров этих обобщенных алгоритмов входит итераторная пара, задающая диапазон элементов вектора, к которым применяется алгоритм. Скажем, чтобы упорядочить все элементы некоторого вектора ivec, достаточно написать следующее:
sort ( ivec.begin(), ivec.end() );
Чтобы применить алгоритм sort() только к первой половине вектора, мы напишем:
sort ( ivec.begin(), ivec.begin() + ivec.size()/2 );
Роль итераторной пары может играть и пара указателей на элементы встроенного массива. Пусть, например, нам дан массив:
int ia[7] = { 10, 7, 9, 5, 3, 7, 1 };
Упорядочить весь массив можно вызовом алгоритма sort():
sort ( ia, ia+7 );
Так можно упорядочить первые четыре элемента:
sort ( ia, ia+4 );
Для использования алгоритмов в программу необходимо включить заголовочный файл
#include
Ниже приведен пример программы, использующей разнообразные алгоритмы в применении к объекту типа vector:
#include
#include
#include
int ia[ 10 ] = {
51, 23, 7, 88, 41, 98, 12, 103, 37, 6
};
int main()
{
vector< int > vec( ia, ia+10 );
vector
cout << "Начальный массив: ";
for ( ; it != end_it; ++ it ) cout << *it << ' ';
cout << "\n";
// сортировка массива
sort( vec.begin(), vec.end() );
cout << "упорядоченный массив: ";
it = vec.begin(); end_it = vec.end();
for ( ; it != end_it; ++ it ) cout << *it << ' ';
cout << "\n\n";
int search_value;
cout << "Введите значение для поиска: ";
cin >> search_value;
// поиск элемента
vector
found = find( vec.begin(), vec.end(), search_value );
if ( found != vec.end() )
cout << "значение найдено!\n\n";
else cout << "значение найдено!\n\n";
// инвертирование массива
reverse( vec.begin(), vec.end() );
cout << "инвертированный массив: ";
it = vec.begin(); end_it = vec.end();
for ( ; it != end_it; ++ it ) cout << *it << ' ';
cout << endl;
}
Стандартная библиотека С++ поддерживает и ассоциативные массивы. Ассоциативный массив – это массив, элементы которого можно индексировать не только целыми числами, но и значениями любого типа. В терминологии стандартной библиотеки ассоциативный массив называется отображением (map). Например, телефонный справочник может быть представлен в виде ассоциативного массива, где индексами служат фамилии абонентов, а значениями элементов – телефонные номера:
#include