Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Использование набора вместе с отображением
В некоторых случаях приходится перебирать весь набор элементов отображения. Для этого понадобится набор ключей к этому отображению:
Обратите внимание, что при использовании вызова Logger метод toString() класса Integer, извлекаемый изотображения, вызывается автоматически. Отображение не возвращает списка своих ключей, потому что каждый ключ уникален, а уникальность – признак набора.
Функционал java коллекций Отображения (ассоциативные массивы) · Map - интерфейс отображений; · SortedMap - расширение Map с возможностью автоматической сортировки ключей; · NavigableMap - расширение SortedMap с приблизительным доступом к элементу. Например, higherKey возвращает наименьший ключ, который больше указанного; · HashMap - класс реализующий отображение. null значение разрешено в качестве ключа и элемента. Без синхронизации; · Hashtable - класс реализующий отображение. null значения запрещены. С поддержкой синхронизации; · LinkedHashMap - расширение HashMap, содержит в себе связанный список для хранения элементов в порядке добавления, удобен в кэшировании данных; · WeakHashMap - специализированное отображение, использующее в качестве значений слабые ссылки. Удобен для кэширования неизменяемых объектов. Без синхронизации. · TreeMap - реализация интерфейса SortedMap, представляет собой красно-черное дерево. Без синхронизации; · EnumMap - специализированное отображение, использующее в качестве ключа перечисление. Без синхронизации. (java 5+) · IdentityHashMap - редко используемая реализация отображения, проверка значения ключа делается через операцию ==, а не метод equal как обычно. Без синхронизации. · Map.Entry - интерфейс для пары ключ/значение, можно получить из итератора; Списки Список представляет собой упорядоченную коллекция. Есть доступ к элементам по индексу. Может содержать одинаковые элементы. · List - расширение Collection для управления упорядоченными коллекциями; · ArrayList - обычный динамический массив. Без синхронизации; · Vector - динамический массив с синхронизацией, а значит чуть медленей; · LinkedList - связной список. Без синхронизации. Более эффективен при частых вставках/удалений элементов в середине, чем ArrayList/Vector. Используется как стек, очередь, двунаправленная очередь; · Stack - расширение Vector для реализации стека; Очередь/стек · Queue - интерфейс для управления очередью (java 5+); · Deque - расширение Queue для управления двунаправленной очередью (java 6+); · ArrayDeque - оптимизированная реализация стека и очереди на основе интерфейса Deque (быстрее Stack и LinkedList). Без синхронизации. (java 5+); · LinkedList - связной список, в последних версиях добавлена реализация интерфейсов Queue и Deque; · PriorityQueue - приоритетная очередь, т.е. элементы могут располагаться в соответствии с указанным алгоритмом сравнения (класс Comparator). (java 5+); Множество Множество представляет собой коллекцию не имеющей одинаковых элементов. · Set - интерфейс для управления множеством; · SortedSet - интерфейс для упорядоченного множества; · NavigableSet - расширение SortedSet с приблизительным доступом к элементу. Например, метод higher позволяет получить наименьшей элемент в множестве, который больше указанного для получение приблизительных (java 5+); · HashSet - реализация множества на основе отображения (в реальности на основе HashMap). Без синхронизации; · LinkedHashSet - расширение HashSet, элементы упорядочены в порядке добавления, внутренне использует связной список. Без синхронизации; · TreeSet - гарантируется логарифмическая зависимость временных затрат на выполнение базовых операций. Интерфейс NavigableSet реализован на основе TreeMap. Без синхронизации.; · EnumSet - специализированное множество для объектов типа перечислений. Без синхронизации;
БИЛЕТ №19 Пространства имен в C++ Глобальные переменные видны в любых точках программы, начиная с объявления переменной, локальные – только в пределах блока. Существуют и статические переменные, объявленные внутри блока, их область видимости также ограничивается этим блоком (не путайте с временем жизни! ). Язык C++ позволяет ограничить действие имен некоторой областью, специально для этого объявленной. Такая область называется пространством имен и создается с помощью ключевого слова namespace. В пространство имен могут быть включены переменные, функции. Создается пространство имен на внешнем уровне: namespace имя { объявления членов пространства } Например, namespace smp { int count; void calc(); } Функции, объявленные в пространстве имен, имеют прямой доступ к другим членам пространства. Для определения функции, принадлежащей пространству имен, используется оператор разрешения области видимости:: (два двоеточия): void smp:: calc() { count++; } Доступ к членам пространства имен извне возможен двумя способами. 1. При помощи явного указания пространства имен и оператора разрешения области видимости: smp:: calc(); cout < < smp:: count < < endl; 2. При помощи предложения using namespace, включающего данное пространство имен в область видимости текущего блока, что позволяет обращаться к членам пространства без указания имени пространства: using namespace smp; calc(); cout < < count < < endl; Если предложение using объявлено на внешнем уровне, видимость членов пространства имен становится глобальной. При появлении языка C++ элементы, объявленные в библиотеке C++, относились к глобальному пространству имен, не имеющему имени. Действующий стандарт языка C++ все эти элементы относит к пространству имен std. Требования стандарта соблюдены в компиляторе, входящем в состав Microsoft Visual C++.NET, поэтому при разработке программы в этой среде для обращения к потокам cin, cout следует использовать один из приведенных выше способов: int a; std:: cout < < " Введите число"; std:: cin > > a; std:: cout < < " Вы ввели" < < a< < std:: endl; или using namespace std; … int a; cout < < " Введите число"; cin > > a; cout < < " Вы ввели" < < a< < endl;
Модули Сам по себе модуль также является отдельной областью видимости. Для доступа к глобальным переменным из другого модуля используется объявление с атрибутом extern, которое обычно размещается в заголовочном файле. Естественно в этом случае, только один модуль должен реально определять это имя.
БИЛЕТ №20 20. Виды конструкторов в С++. Деструкторы в C++. конструктор по умолчанию — конструктор, не принимающий аргументов; конструктор копирования — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него); конструктор преобразования — конструктор, принимающий один аргумент (эти конструкторы могут вызываться автоматически для преобразования значений других типов в объекты данного класса). class Complex { public: // Конструктор по умолчанию // (в данном случае является также и конструктором преобразования) Complex(double i_re = 0, double i_im = 0) : re(i_re), im(i_im) {}
// Конструктор копирования Complex(const Complex & obj) { re = obj.re; im = obj.im; } private: double re, im; }; [править] Конструктор по умолчанию Основная статья: Конструктор по умолчанию Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается). [править] Конструктор копирования Основная статья: Конструктор копирования Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению. Конструктор копирования в основном необходим, когда объект имеет указатели на объекты, выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе. Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования. [править] Конструктор преобразования Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально. Конструкторы и деструкторы – специальные функции класса, предназначенные для инициализации данных классов. Конструктор представляет собой специальную функцию, которую ваша программа автоматически вызывает каждый раз при создании объекта. Конструктор имеет такое же имя, как и класс объекта.Конструктор не имеет возвращаемого значения, но вы не указываете ему тип void. Вместо этого вы просто не указываете возвращаемое значение вообще.Когда ваша программа создает объект, она может передать параметры конструктору во время объявления объекта.C++ позволяет вам перегружать конструкторы и разрешает использовать значения по умолчанию для параметров.Деструктор представляет собой специальную функцию, которую ваша программа вызывает автоматически каждый раз при уничтожении объекта. Деструктор имеет такое же имя, как и класс объекта, но его имя предваряется символом тильды (~).Конструкторы не могут быть virtual и static. Деструктор может быть virtual, но не может быть static.
Class stack { Chart * Int m; Int top; Public: Stack(int); Stack(); Stack(chart*, int); ~stack(); } Stack:: stack() {s=new char[512] m=512; top =0} БИЛЕТ №21
Перегрузка функций – один из основных аспектов языка C++. Она обеспечивает статический полиморфизм, а также гибкость и комфорт. Чаще всего перегрузке подвергаются конструкторы. Одной из наиболее важных форм перегрузки конструкторов является конструктор копирования. Возможность задавать аргументы по умолчанию используют в качестве альтернативы перегрузке функций. Перегрузка функций – это использование одного имени для нескольких функций. Секрет перегрузки заключается в том, что каждое переопределение функции должно использовать либо другие типы параметров, либо другое их количество. Только эти различия позволяют компилятору определить, какую функцию следует вызвать в том или ином случае. #include < iostream> using namespace std; int myfunc (int i); double myfunc (double i); int main () { count < < myfunc (10)< < ” ”; //Вызов функции int myfunc (int i) count < < myfunc (5.4); //Вызов функции double myfunc (double i) return 0; } double myfunc (double i); { return i; } int myfunc (int i) { return i; } В следующей программе перегруженные варианты функции myfunc () используют разное количество параметров. #include < iostream> using namespace std; int myfunc (int i); int myfunc (int i, int j); int main () { count< < myfunc(10)< < ” “; //Вызов функции int myfunc (int i) count< < myfunc(4, 5); //Вызов функции int myfunc (int i, int j) return 0; } Int myfunc (int i) { return i; } Int myfunc(int i, int j) { return i*j; } Следует помнить, что перегруженные функции должны отличаться типами или количеством параметров. Тип возвращаемого значения не позволяет перегружать функции. Например, следующий вариант перегрузки функции myfunc() неверен. Int myfunc(int i); func (int i); Иногда объявления двух функций внешне отличаются, но фактически совпадают. Рассмотрим в качестве примера следующие объявления. void f(int *p); void f (int p[]); Следует помнить, что компилятор не различает выражения *p и p[]. Следовательно, хотя внешне два прототипа функции f различаются, на самом деле они полностью совпадают.
С перегрузкой функций тесно связан механизм перегрузки операторов. В языке C++ можно перегрузить большинство операторов, настроив их на конкретный класс. Перегруженный оператор сохраняет своё первоначальное предназначение. Просто набор типов, к которым его можно применить, расширяется. Перегрузка операторов осуществляется с помощью операторных функций, которые определяют действия перегруженных операторов применительно к соответствующему классу. Операторные функции создаются с помощью ключевого слова operator. Операторные функции могут быть как членами класса, так и обычными функциями. Однако обычные операторные функции, как правило, объявляют дружественными по отношению к классу, для которого они перегружают оператор. В каждом из этих случаев операторная функция объявляется по-разному. Операторная функция-член имеет следующий вид: тип_возвращаемого_значения имя_класса: : operator # (список_аргументов) { ………// Операции } Обычно операторная функция возвращает объект класса, с которым она работает, однако тип возвращаемого значения может быть любым. Символ # заменяется перегружаемым оператором. При перегрузке унарного оператора список_аргументов остаётся пустым. При перегрузке бинарного оператора список список_аргументов содержит один параметр. Эта программа создаёт класс loc, в котором храняться географические координаты. В ней перегружается оператор «+». #include < iostream> using namespacestd; class loc { int longitude, latitude; public: loc () {} loc (int lg, int lt){ longitude=lg; latitude=lt; } void show() { count < < longitude< < “ “; count < < latitude < < “/n”; } Loc operator+(loc op2); }; loc loc: : operator+(loc op2) { loc temp; temp.longitude=ob2.longitude + longitude; temp.latitude=op2.latitude + latitude; return temp; } int main() { loc ob1 (10, 20), ob2 (5, 30); ob1.show(); //Выводит на экран числа 10 20 ob2.show(); //Выводит на экран числа 5 30 ob1=ob1+ob2; ob1.show(); //Выводит на экран числа 15 50 return 0; } Как видим, функция operator+() имеет только один параметр, несмотря на то, что она перегружает бинарный оператор. Причина заключается в том, что операнд, стоящий в левой части оператора, передаётся операторной функции неявно с помощью указателя this. Отсюда следует важный вывод: при перегрузке бинарного оператора вызов операторной функции генерируется объектом, стоящим в левой части оператора. Кроме того, поскольку операторная функция operator+() возвращает объект типа loc, допускается следующее выражение: (ob1+ob2).show(); В этой ситуации операторная функция создаёт временный объект, который уничтожается после возвращения из функции show(). Следует помнить, что операторная функция может возвращать объекты любых типов, и что эти типы зависят только от конкретного приложения. Однако, как правило, операторные функции возвращают объекты класса, с которым они работают. Общая форма префиксной и постфиксной операторных функций: //префиксный оператор тип operator#(){ //тело префиксного оператора } //Постфиксный оператор тип operator#(int i){ //тело постфиксного оператора } Сокращённые операторы присваивания также можно перегружать. loc loc: : operator+=(loc op2) { longitude=op2.longitude + longitude; latitude=ob2.latitude + latitude; return *this; } Нельзя перегружать операторы: «.», «: : », «.*», «? ». Операторы можно перегружать с помощью дружественных функций, не являющихся членами класса. Это значит, что дружественные функции не получают неявного указателя this. Следовательно, перегруженная операторная функция получает параметры явно. Таким образом, при перегрузке бинарного оператора дружественная функция получает два параметра, а при перегрузке унарного оператора – один. Первым параметром дружественной функции, перегружающий бинарный оператор, является его левый операнд, а вторым – правый операнд. БИЛЕТ №22 Шаблоны C++ Шабло́ ны— средство языка C++, предназначенное для кодирования обобщённых алгоритмов, без привязки к некоторым параметрам (например, типам данных, размерам буферов, значениям по умолчанию). В C++ возможно создание шаблонов функций и классов. Шаблоны позволяют создавать параметризованные классы и функции. Параметром может быть любой тип или значение одного из допустимых типов (целое число, enum, указатель на любой объект с глобально доступным именем). Например, нам нужен какой-то класс: class SomeClass{ int SomeValue; int SomeArray[20]; ... } Для одной конкретной цели мы можем использовать этот класс. Но, вдруг, цель немного изменилась, и нужен еще один класс. Теперь нужно 30 элементов массива SomeArray и вещественный тип SomeValue и элементов SomeArray. Тогда мы можем абстрагироваться от конкретных типов и использовать шаблоны с параметрами. Синтаксис: в начале перед объявлением класса напишем слово template и укажем параметры в угловых скобках. В нашем примере: template < int ArrayLength, typename SomeValueType > class SomeClass{ SomeValueType SomeValue; SomeValueType SomeArray[ ArrayLength ]; ... } Тогда для первой модели пишем: SomeClass < 20, int > SomeVariable; для второй: SomeClass < 30, double > SomeVariable2; Хотя шаблоны предоставляют краткую форму записи участка кода, на самом деле их использование не сокращает исполнимый код, так как для каждого набора параметров компилятор создаёт отдельный экземпляр функции или класса. Синтаксис описания шаблона Шаблон функции начинается с ключевого слова template, за которым в угловых скобках следует список параметров. Затем следует объявление функции: template< typename T > void sort( T array[], int size ); // прототип: шаблон sort объявлен, но не определён
template< typename T > void sort( T array[], int size ) // объявление и определение { T t; for (int i = 0; i < size - 1; i++) for (int j = size - 1; j > i; j--) if (array[j] < array[j-1]) { t = array[j]; array[j] = array[j-1]; array[j-1] = t; } }
template< int BufferSize > // целочисленный параметр char* read() { char *Buffer = new char[ BufferSize ]; /* считывание данных */ return Buffer; } Ключевое слово typename появилось сравнительно недавно, поэтому стандарт[1] допускает использование class вместо typename: template< class T > Вместо T допустим любой другой идентификатор. [править]Пример использования Простейшим примером служит определение минимума из двух величин. Если a меньше b то вернуть а, иначе - вернуть b В отсутствие шаблонов программисту приходится писать отдельные функции для каждого используемого типа данных. Хотя многие языки программирования определяют встроенную функцию минимума для элементарных типов (таких как целые и вещественные числа), такая функция может понадобиться и для сложных (например «время» или «строка») и очень сложных («игрок» в онлайн-игре) объектов. Так выглядит шаблон функции определения минимума: template< typename T > T min( T a, T b ) { return a < b? a: b; } Для вызова этой функции можно просто использовать её имя: min( 1, 2 ); min( 'a', 'b' ); min( string( " abc" ), string( " cde" ) ); Вызов шаблонной функции Вообще говоря, для вызова шаблонной функции, необходимо указать значения для всех параметров шаблона. Для этого после имени шаблона указывается список значений в угловых скобках: int i[5] = { 5, 4, 3, 2, 1 }; sort< int > ( i, 5 );
char c[] = " бвгда"; sort< char > ( c, strlen( c ) );
sort< int > ( c, 5 ); // ошибка: у sort< int > параметр int[] а не char[]
char *ReadString = read< 20 > (); delete [] ReadString; ReadString = read< 30 > (); Для каждого набора параметров компилятор генерирует новый экземпляр функции. Процесс создания нового экземпляра называется инстанцированием шаблона. В примере выше компилятор создал две специализации шаблона функции sort (для типов char и int) и две специализации шаблона read (для значений BufferSize 20 и 30). Последнее скорее всего расточительно, так как для каждого возможного значения параметра компилятор будет создавать новые и новые экземпляры функций, которые будут отличаться лишь одной константой. Выведение значений параметров В некоторых случаях компилятор может сам вывести (логически определить) значение параметра шаблона функции из аргумента функции. Например, при вызове вышеописанной функции sort необязательно указывать параметр шаблона (если он совпадает с типом элементов аргумента-массива): int i[5] = { 5, 4, 3, 2, 1 }; sort( i, i + 5 ); // вызывается sort< int >
char c[] = " бвгда"; sort( c, c + strlen( c ) ); // вызывается sort< char > Возможно выведение и в более сложных случаях. В случае использования шаблонов классов с целыми параметрами также возможно выведение этих параметров. Например: template< int size > class IntegerArray { int Array[ size ]; /*... */ }; template< int size > // Прототип шаблона void PrintArray( IntegerArray< size > array ) { /*... */ } // Вызов шаблона
// Использование объекта шаблона IntegerArray< 20> ia; PrintArray( ia ); Правила выведения введены в язык для облегчения использования шаблона и для избежания возможных ошибок, например попытка использования sort< int > для сортировки массива символов. Если параметр шаблона можно вывести по нескольким аргументам, то результат выведения должен быть в точности одинаков для всех этих аргументов. Например, следующие вызовы ошибочны: min (0, 'a'); min (7, 7.0); [править]Ошибки в шаблонах Некоторые ошибки в описании шаблона могут быть выявлены уже в месте описания. Эти ошибки не зависят от конкретных параметров. Например: template< class T > void f( T data ) { T *pt = 7; // ошибка: инициализация указателя целым числом datA = 0; // ошибка: неизвестный идентификатор datA *pt = data // ошибка: нет точки с запятой } Ошибки, связанные с использованием конкретных параметров шаблона, нельзя выявить до того, как шаблон использован. Например, шаблон min сам по себе не содержит ошибок, однако использование его с типами, для которых операция '< ' не определена, приведёт к ошибке: struct A { int a; }; A obj1, obj2; min( obj1, obj2 ); Если ввести операцию '< ' до первого использования шаблона, то ошибка будет устранена. Так проявляется гибкость шаблонов в C++: friend inline bool operator< ( const A& a1, const A& a2 ) { return a1.a < a2.a; }
min( obj1, obj2 ); Шаблоны классов
В классе, реализующем связный список целых чисел, алгоритмы добавления нового элемента списка, поиска нужного элемента не зависят от того, что элементы списка — целые числа. Те же алгоритмы применялись бы и для списка символов, строк, дат, классов игроков, и так далее. template< class T > class List { /*... */ public: void Add( const T& Element ); bool Find( const T& Element ); /*... */ }; [править]Использование шаблонов Для использования шаблона класса, необходимо указать его параметры: List< int> li; List< string> ls; li.Add( 17 ); ls.Add( " Hello! " ); Технические подробности Параметры шаблонов Параметрами шаблонов могут быть: параметры-типы, параметры обычных типов, параметры-шаблоны. Для параметров любого типа можно указывать значения по умолчанию. template< class T1, // параметр-тип typename T2, // параметр-тип int I, // параметр обычного типа T1 DefaultValue, // параметр обычного типа template< class > class T3, // параметр-шаблон class Character = char // параметр по умолчанию > [править]Параметры-шаблоны Если в шаблоне класса или функции необходимо использовать один и тот же шаблон, но с разными параметрами, то используются параметры-шаблоны. Например: template< class Type, template< class > class Container > class CrossReferences { Container< Type > mems; Container< Type* > refs; /*... */ }; CrossReferences< Date, vector > cr1; CrossReferences< string, set > cr2; БИЛЕТ №23 Чтобы комфортно работать с исключениями в С++ вам нужно знать лишь три ключевых слова:
А теперь пример, демонстрирующий, как применить то, что вы узнали: void func(){ try { throw 1; } catch(int a) { cout < < " Caught exception number: " < < a < < endl; return; } cout < < " No exception detected! " < < endl; return; }Если выполнить этот фрагмент кода, то мы получим следующий результат: Caught exception number: 1Теперь закоментируйте строку throw 1; и функция выдаст такой результат: No exception detected!Как видите все очень просто, но если это применить с умом, такой подход покажется вам очень мощным средством обработки ошибок. Catch может " ловить" любой тип данных, так же как и throw может " кинуть" данные любого типа. Т.е. throw AnyClass(); будет правильно работать, так же как и catch (AnyClass & d) {};. Как уже было сказано, catch может " ловить" данные любого типа, но вовсе не обязательно при это указывать переменную. Т.е. прекрасно будет работать что-нибудь типа этого: catch(dumbclass) { }так же, как и catch(dumbclass& ) { }Так же можно " поймать" и все исключения: catch(...) { }Троеточие в этом случае показывает, что будут пойманы все исключения. При таком подходе нельзя указать имя переменной. В случае, если " кидаются" данные нестандартного типа (экземпляры определенных вами классов, структур и т.д.), лучше " ловить" их по ссылке, иначе вся " кидаемая" переменная будет скопирована в стек вместо того, чтобы просто передать указатель на нее. Если кидаются данные нескольких типов и вы хотите поймать конкретную переменную (вернее, переменную конкретного типа), то можно использовать несколько блоков catch, ловящих " свой" тип данных: try { throw 1; // throw 'a'; }catch (long b) { cout < < " пойман тип long: " < < b < < endl; }catch (char b) { cout < < " пойман тип char: " < < b < < endl; }"Создание" исключений Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий catch. Если оператор catch не найден, STL будет обрабатывать исключение в стандартном обработчике, который делает все менее изящно, чем могли бы сделать вы, показывая какие-то непонятные (для конечного пользователя) сообщения и обычно аварийно завершая программу. Однако более важным моментом является то, что пока просматривается стек функций, вызываются деструкторы всех локальных классов, так что вам не нужно забодиться об освобождении памяти и т.п. Перегрузка глобальных операторов new/delete А сейчас хотелось бы отправить вас к статье " Как обнаружить утечку памяти". В ней рассказывается, как обнаружить неправильное управление распределением памяти в вашей программе. Вы можете спросить, при чем тут перегрузка операторов? Если перегрузить стандартные new и delete, то открываются широкие возможности по отслеживанию ошибок (причем ошибок часто критических) с помощью исключений. Например: char *a; try{ a = new char[10]; }catch (...){ // a не создан - обработать ошибку распределения памяти, // выйти из программы и т.п.}// a успешно создан, продолжаем выполнениеЭто, на первый взгляд, кажется длиннее, чем стандартная проверка в С " а равен NULL? ", однако если в программе выделяется десяток динамических переменных, то такой метод оправдывает себя. Популярное:
|
Последнее изменение этой страницы: 2016-07-14; Просмотров: 637; Нарушение авторского права страницы