|
|
Строка 1: |
Строка 1: |
− | В этой главе будут рассмотрены базовые типы данных, применяющиеся в языке К++. Большинство из них объявлены в стандартной библиотеке Gide, однако некоторые, например интервалы, описываются в системной библиотеке самого языка К++. Еще раз напомним Читателю, что в языке К++, стандартные типы данных не являются встроенными. Конечно, компилятор опирается на них при генерации кода, но это совершенно не означает, что их нужно воспринимать как что-то единожды определенное и неизменное. Как уже было показано в книге, как с точки зрения компилятора так и самой виртуальной машины, эти классы ничем не отличаются от обычных, пользовательских классов, когда дело касается их использования на высоком уровне. Если забежать вперед, то можно отметить, что на низком уровне они реализованы на языке C++ (из соображений производительности) и их интерфейсы представлены в стандартной библиотеке. Тем не менее, существует возможность их дополнения программистом-пользователем, что и было проделано в главах, посвященных [[Классы и объекты#Расширения|расширениям]]. Разумеется, от этих классов возможно наследовать собственные классы, точно так же, как и от любых других. В этой главе мы рассмотрим стандартные типы данных с точки зрения их применения и укажем некоторые свойства которые не были упомянуты в ходе повествования.
| + | To think, I was cofnsued a minute ago. |
− | | + | |
− | It's really great that people are sharing this ifnmoration.
| + | |
− | | + | |
− | == Числа с плавающей точкой ==
| + | |
− | | + | |
− | Для проведения сложных математических рассчетов, одних целых чисел недостаточно. Для представления действительных чисел, или чисел с плавающей точкой (запятой), как их называют в информатике, в стандартной библиотеке создан класс <tt>real</tt>, который на низком уровне представлен типом двойной точности (double).
| + | |
− | | + | |
− | В отличие от класса <tt>string</tt>, класс <tt>real</tt> корректно обрабатывается в сочетании с целочисленными классами. Например, следующие выражения дадут одинаковые результаты:
| + | |
− | <source lang=kpp>
| + | |
− | var i = 3 + 0.1415
| + | |
− | var j = 0.1415 + 3
| + | |
− | </source>
| + | |
− | | + | |
− | В результате, обе переменных будут иметь тип <tt>real</tt> и значение <!--примерно -->3,1415. Это достигается тем, что в классе <tt>int</tt> есть специальные версии арифметических операторов, которые принимают в качестве параметра объекты класса <tt>real</tt>.
| + | |
− | | + | |
− | Articles like this really garese the shafts of knowledge.
| + | |
− | | + | |
− | Right on-this helped me sort thigns right out.
| + | |
− | | + | |
− | == Интервалы ==
| + | |
− | | + | |
− | В некоторых случаях бывает необходимо передать в качестве параметра не отдельное значение а диапазон. Для указания диапазона обычно достаточно указать только его границы. Это может осуществляться с помощью класса <tt>interval</tt>. К++ предоставляет специальный синтаксис для указания интервалов с помощью оператора "<tt>..</tt>"; как уже было показано выше, это может применяться для выборки подстрок, соответствующих заданному диапазону индексов, либо подмассивов по тому же принципу:
| + | |
− | <source lang=kpp>
| + | |
− | var s = "abcdef"[0..2]; // "abc"
| + | |
− | var a = [1, 2, 3, 4][1..-1]; // [2, 3, 4]
| + | |
− | </source>
| + | |
− | | + | |
− | Вообще, интервалы могут быть не только численными. В качестве объектов, формирующих интервал, могут выступать любые два объекта, имеющие одинаковые типы:
| + | |
− | <source lang=kpp>
| + | |
− | var left = MyClass.Create(5), right = MyClass.Create(10);
| + | |
− | var i = interval.create(left, right);
| + | |
− | var alpahbet = 'a' .. 'z';
| + | |
− | </source>
| + | |
− | | + | |
− | Интервалы могут использоваться как виртуальные массивы, то есть к ним можно обращаться как к массиву, запрашивая некоторый элемент по индексу. И он будет возвращен, как будто действительно хранится в массиве (на сама деле, он создается в момент обращения по некоторому известному закону):
| + | |
− | <source lang=kpp>
| + | |
− | var numbers = 1 .. 100;
| + | |
− | var alpahbet = 'a' .. 'z';
| + | |
− | var x = numbers[50]; // x = 1 + (50-1) = 50.
| + | |
− | var y = alphabet[10]; // y = (#a + (10-1)).char = 'k'
| + | |
− | </source>
| + | |
− | | + | |
− | Преимуществом такого подхода является то, что не нужно хранить весь массив в памяти. Зная закон изменения элементов, можно получить любой элемент, зная базу (одну из границ) и индекс элемента.
| + | |
− | | + | |
− | Подобно массивам, интервалы так же обладают методом <tt>each()</tt>, позволяющем проходить по всему массиву, выполняя некоторый блок с параметром текущего элемента массива:
| + | |
− | <source lang=kpp>
| + | |
− | var s = 0; //здесь будет сумма
| + | |
− | var numbers = 1 .. 100;
| + | |
− | numbers.each() { |x| s += x; };
| + | |
− | </source>
| + | |
− | | + | |
− | Для выяснения принадлежности некоторого объекта к интервалу могут применяться метод <tt>contains()</tt> или соответствующий ему оператор <tt>'''in'''</tt>:
| + | |
− | <source lang=kpp>
| + | |
− | var numbers = 1 .. 100;
| + | |
− | var lowercase = 'a' .. 'z';
| + | |
− | var x = numbers.contains(75) ? "yes" : "no"; //x = "yes"
| + | |
− | var y = 'X' in lowercase ? "yes" : "no"; //y = "no"
| + | |
− | </source>
| + | |
− | | + | |
− | Для создания интервалов на базе собственных классов необходимо перегрузить конструктор, а так же соответствующие методы, своими реализациями.
| + | |
− | | + | |
− | SbHObt <a href="http://stqyiclfbwlh.com/">stqyiclfbwlh</a>
| + | |
− | | + | |
− | == Хеши ==
| + | |
− | | + | |
− | Хеши являются еще одной формой контейнеров. Подобно массивам и спискам, они могут хранить в себе другие объекты, однако они отличаются и от тех и от других. Для доступа к элементу списка, необходимо использовать итераторы, в то время как для ссылки на элемент массива применяются индексы — числа, которые соответствуют номеру элемента в массиве.
| + | |
− | | + | |
− | В этом отношении, хеши похожи на массивы, однако, для доступа к элементу используются не индексы, а совершенно произвольные объекты; или если перефразировать наоборот, то: для доступа к некоторому элементу хеша применяются индексы, которые могут быть представлены любым объектом: числом, строкой либо экземпляром пользовательского класса.
| + | |
− | | + | |
− | Хеши обычно применяются там, где требуется организовать связь нескольких объектов. При этом, первому объекту-индексу, который в хешах называется ''ключом'', ставится в соответствие другой объект — ''значение''. Хеши хранят множество таких пар ключ-значение и, подобно спискам и массивам, предоставляют удобные методы для работы со своим содержимым.
| + | |
− | | + | |
− | Чаще всего, хеши применяются для связи текстовых строк или для организации ''ассоциативных массивов'', у которых в качестве индекса применяется строка. Приведем несколько примеров применения хешей. Допустим, мы хотим поставить в соответствие названию дня недели его порядковый номер. То есть, строке "понедельник" должно соответствовать число 1, "вторнику" — 2 и так далее. Конечно, эту задачу можно решить и традиционным способом, например с помощью массивов:
| + | |
− | <source lang=kpp>
| + | |
− | const weekdays = ['понедельник', 'вторник', 'среда', 'четверг',
| + | |
− | 'пятница', 'суббота', 'воскресенье'];
| + | |
− | | + | |
− | function int WeekDay2DayNr(const string weekday) {
| + | |
− | var result; //результат (динамическая переменная)
| + | |
− | weekdays.each_pair() { |nr, day|
| + | |
− | if (day == weekday) {
| + | |
− | result = nr + 1;
| + | |
− | break;
| + | |
− | }
| + | |
− | };
| + | |
− | if (result) //если переменной присвоено значение
| + | |
− | return result; //возвращаем его
| + | |
− | else //иначе, сообщаем об ошибке
| + | |
− | throw EInvalidCall.create('указанная строка не является днем недели');
| + | |
− | }
| + | |
− | </source>
| + | |
− | | + | |
− | В этом примере, мы заводим массив ''weekdays'', в который помещаем названия всех дней недели.
| + | |
− | | + | |
− | В функции <tt>WeekDay2DayNr()</tt> мы перебираем все элементы массива и сравниваем их с контрольной строкой. Если происходит совпадение, то мы сохраняем текущее значение индекса (''nr'') в динамическую переменную ''result'' и прерываем цикл с помощью оператора <tt>'''break'''</tt>.
| + | |
− | | + | |
− | Далее, с помощью условного оператора проверяется, было ли переменной ''result'' присвоено некоторое значение. Если было, то оно возвращается как результат функции; если нет (то есть, оно по прежнему равно <tt>'''null'''</tt>) — генерируется [[Идеология языка#Понятие исключения|исключение]].
| + | |
− | | + | |
− | Данная функция будет исправно работать, однако в реальных условиях, при большом количестве элементов массива, операция поиска перебором может стать очень медленной. В таком случае, наилучшим решением, будет использование хешей. Перепишем предыдущую функцию так, чтобы она использовала хеш вместо массива:
| + | |
− | <source lang=kpp>
| + | |
− | const weekdays = {'понедельник' => 1, 'вторник' => 2, 'среда' => 3,
| + | |
− | 'четверг' => 4, 'пятница' => 5, 'суббота' => 6,
| + | |
− | 'воскресенье' => 7};
| + | |
− | | + | |
− | function int WeekDay2DayNr(const string day) {
| + | |
− | try {
| + | |
− | return weekdays[day];
| + | |
− | } catch (ERangeError e) {
| + | |
− | throw EInvalidCall.create('указанная строка не является днем недели');
| + | |
− | }
| + | |
− | }
| + | |
− | </source>
| + | |
− | | + | |
− | Посмотрите, насколько проще стал код. А главное, он стал намного быстрее! Хеши позволяют значительно повысить скорость доступа к информации за счет того, что вместо обычного перебора, применяются альтернативные методы. Если говорить кратко, то происходит операция "перемешивания" ключа, которая осуществляется с помощью [http://ru.wikipedia.org/wiki/Хеш-функция хеш-функции], в результате которой получается некоторое значение — хеш ключа. Это значение уже используется для выборки искомого объекта. Скорость операции возрастает за счет того, что от медленной операции сравнения ключей, мы переходим к быстрой операции сравнения хешей. Хеши изначально проектируются так, чтобы их было легко сравнивать; в то же время, они должны быть различными для разных значний ключа. Более подробно, про хеши и хеширование, можно [http://ru.wikipedia.org/wiki/Хеш-таблица почитать на Википедии].
| + | |
− | | + | |
− | Для объявления хеш-таблицы ''weekdays'' применяется специальный синтаксис, встроенный в язык К++: необходимо в фигурных скобрах перечислить пары ключ-значение, отделяя их запятой. Для связи ключа и значения в паре, применяется оператор соответствия (<tt>=></tt>). Подобно массивам, объекты хеш-таблиц могут объявляться где угодно в коде, будь то инициализатор переменной, либо фактический параметр в коде вызова функции. В нашем случае, объект создается в инициализаторе [[константы]], которая будет иметь тип <tt>hash</tt>.
| + | |
− | | + | |
− | Разумеется, для заполнения хеша можно применять и обычную операцию создания объекта, с последующим вызовом методов <tt>insert()</tt> для добавления информации в таблицу:
| + | |
− | <source lang=kpp>
| + | |
− | var my_config = new hash;
| + | |
− | my_config.insert('verbosity', 3);
| + | |
− | my_config.insert('user name', 'nobody');
| + | |
− | my_config.insert('password', 'y&5#3Eff_');
| + | |
− | //...
| + | |
− | </source>
| + | |
− | | + | |
− | Для заполнения хеша можно применять так же оператор индексного доступа:
| + | |
− | <source lang=kpp>
| + | |
− | my_config[:path] = 'diss:/etc/myconf';
| + | |
− | </source>
| + | |
− | | + | |
− | Между вызовом метода <tt>insert()</tt> и использованием оператора <tt>[]=</tt> есть небольшая разница. Она заключается в том, что случае, если в хеше уже присутствует пара значений с тем же ключом, то метод <tt>insert()</tt> сгенерирует [[Идеология языка#Понятие исключения|исключение]], в то время как оператор <tt>[]=</tt> заменит старое значение на новое. Существует так же метод <tt>replace()</tt>, который ведет себя подобно вышеозначенному оператору:
| + | |
− | <source lang=kpp>
| + | |
− | my_config[:enabled] = 'true';
| + | |
− | my_config.replace('enabled', 'true');
| + | |
− | </source>
| + | |
− | | + | |
− | | + | |
− | Хеши, подобно массивам и спискам, обладают набором методов для перебора их содержимого, однако смысл некоторых методов немного отличается от своих аналогов в массивах:
| + | |
− | <source lang=kpp>
| + | |
− | weekdays.each() { |day, nr| print("key: #{day}, value: #{nr}\n"); };
| + | |
− | var keys = weekdays.keys();
| + | |
− | keys.each() { |key| println(key); };
| + | |
− | weekdays.values().each() { |value| print(value as string + ' '); };
| + | |
− | </source>
| + | |
− | | + | |
− | В этом примере используются три различных метода, которые вызывают блок с парой параметров ключ-значение (метод <tt>each()</tt>), либо возвращают массивы ключей и значений (методы <tt>keys()</tt> и <tt>values()</tt> соответственно). Во второй и третьей строках, показывается способ отображения всех ключей хеша, с помощью метода <tt>keys()</tt> и промежуточной переменной. В последней строке вызовы методов совмещены в одной конструкции. Для наглядности приведем вывод, каким он должен был бы быть, при выполнении этого кода (подразумевается использование хеша ''weekdays'' из предыдущих примеров):
| + | |
− | | + | |
− | key: понедельник, value: 1
| + | |
− | key: вторник, value: 2
| + | |
− | key: среда, value: 3
| + | |
− | key: четверг, value: 4
| + | |
− | key: пятница, value: 5
| + | |
− | key: суббота, value: 6
| + | |
− | key: воскресенье, value: 7
| + | |
− | понедельник
| + | |
− | вторник
| + | |
− | среда
| + | |
− | четверг
| + | |
− | пятница
| + | |
− | суббота
| + | |
− | воскресенье
| + | |
− | 1 2 3 4 5 6 7
| + | |
− | | + | |
− | '''Примечание 1:''' Здесь мы перечислили выводимые значения по порядку. Однако, на практике такого не случается. За счет операции перемешивания, порядок следования элементов теряется. Поэтому, если требуется сохранить порядок, необходимо либо где-то записывать очредность ключей, либо сортировать их при обработке. Например, код обработки элементов хеша в порядке очередности ключей может выглядеть примерно так:
| + | |
− | <source lang=kpp>
| + | |
− | my_hash.keys().sort().each() { |key| print("key: #{key}, value: #{my_hash[key]}\n"); };
| + | |
− | </source>
| + | |
− | | + | |
− | '''Примечание 2:''' Как уже было сказано выше, хеши позволяют использовать объекты любого типа как в качестве ключа, так и в качестве значения. Для того, чтобы можно было указывать в качестве ключа объект пользовательского типа, он должен иметь реализацию метода <tt>hash()</tt>, не принимающего параметров и возвращающего объект класса <tt>int</tt>. Дополнительно, класс объектов ключа должен предоставлять операторы сравнения и присвоения (<tt>==</tt> и <tt>=</tt>). На объекты, содержащиеся в значениях, никаких ограничений не накладывается.
| + | |
− | | + | |
− | Таким образом, реализация класса ключей должена выглядеть примерно так:
| + | |
− | <source lang=kpp>
| + | |
− | class MyClass {
| + | |
− | public:
| + | |
− | export const function int hash() {
| + | |
− | /* здесь идет код вычисления хеша */
| + | |
− | }
| + | |
− | | + | |
− | const operator bool == (const MyClass x) {
| + | |
− | /* код сравнения инстанций */
| + | |
− | }
| + | |
− |
| + | |
− | operator MyClass = (const MyClass src) {
| + | |
− | /* код присвоения */
| + | |
− | return this;
| + | |
− | }
| + | |
− | /* другие методы */
| + | |
− | }
| + | |
− | </source>
| + | |
− | | + | |
− | == Указатели ==
| + | |
To think, I was cofnsued a minute ago.