- •Раздел 4. Разработка по Тема 4.1. Проектирование интерфейса с пользователем
- •4.1.1. Типы пользовательских интерфейсов.
- •4.1.2. Пользовательская и программная модели интерфейса.
- •4.1.3. Разработка диалогов.
- •4.1.4. Основные компоненты графических пользовательских интерфейсов.
- •Тема 4.2. Реализация графических пользовательских интерфейсов.
- •4.2.1. Диалоги, управляемые пользователем.
- •4.2.2. Диалоги, управляемые системой.
- •4.2.3. Использование метафор.
- •4.2.4. Технология Drag and Drop.
- •4.2.5. Интеллектуальные элементы.
- •4.3.1. Базовые типы данных.
- •Константы
- •Область действия имен
- •4.3.2. Указатели и адресная арифметика.
- •4.3.3. Составные типы данных. Структуры
- •Битовые поля
- •Определение типов
- •Перечислимые типы
- •4.3.4. Выражения и операции.
- •4.3.5. Управляющие конструкции. Условные операторы
- •Операторы циклов
- •4.4.1. Статические одномерные массивы.
- •4.4.2. Статические многомерные массивы.
- •4.4.3. Динамические массивы.
- •4.4.4. Массивы указателей.
- •4.5.1. Стеки.
- •4.5.2. Очереди.
- •4.5.3. Списки.
- •4.5.4. Бинарные деревья.
- •4.6.1. Объявление классов и экземпляров классов.
- •4.6.2. Инкапсуляция данных и методов.
- •4.6.3. Конструкторы классов.
- •Конструктор по умолчанию
- •Конструктор копирования
- •4.6.4. Деструкторы классов.
- •4.7.1. Разделы в описании класса.
- •4.7.2. Friend-конструкции.
- •4.7.3. Статические члены классов.
- •4.7.4. Использование описателя const в классах.
- •4.8.1. Вложенность классов.
- •4.8.2. Наследование данных и методов.
- •4.8.3. Типы наследования.
- •4.9.1. Полиморфизм раннего связывания.
- •4.9.2. Полиморфизм позднего связывания и виртуальные функции.
- •4.9.3. Абстрактные методы и классы.
- •4.10.1. Функции консольного ввода-вывода.
- •4.10.2. Функции файлового ввода-вывода.
- •4.10.3. Использование библиотеки классов потокового ввода-вывода.
- •4.11.1. Перегрузка операций.
- •4.11.2. Шаблоны функций.
- •4.11.3. Шаблоны классов.
- •4.11.4. Обработка исключений.
- •Тема 4.12. Com-технология.
- •4.12.1. Основные понятия.
- •4.12.2. Типы интерфейсов.
- •Свойства интерфейсов
- •Типы интерфейсов
- •4.12.3. Типы com-объектов.
- •4.12.4. Фабрика классов.
- •Тема 4.13. Построение com-сервера.
- •4.13.1. Язык idl.
- •Содержимое файла idl
- •4.13.2. Определение пользовательского интерфейса.
- •4.13.3. Реализация пользовательского интерфейса.
- •4.13.4. Создание тестового клиента.
- •Тема 4.14. Обзор платформы ms .Net.
- •4.14.1. Общая идея архитектуры .Net.
- •4.14.2. Достоинства и недостатки .Net.
- •4.14.3. Схема трансляции программ в .Net.
- •4.14.4. Язык msil.
- •4.14.5. Объектно-ориентированная модель .Net.
4.4.3. Динамические массивы.
При решении на компьютере серьезных задач важно экономно расходовать имеющуюся память и освобождать ее по мере возможности. Принцип организации динамического двухмерного массива очень похож на принцип организации статического двухмерного массива. Отличие состоит в том, что теперь для адресов а, а[0], а[1],..., а[n-1] должно быть отведено реальное физическое пространство памяти, в то время как для статического двухмерного массива выражения вида а, а[0], а[1],..., а[n-1] были лишь возможными конструкциями для ссылок на реально существующие элементы массива, но сами эти указатели не существовали как объекты в памяти компьютера.
Алгоритм выделения памяти таков:
Определяем переменную а как адрес массива адресов: float **a;.
Захватываем память из области heap для массива из n указателей на тип float и присваиваем адрес начала этой памяти указателю а. Оператор, выполняющий это действие, выглядит так: а = new float* [n];.
В цикле пробегаем по массиву адресов а[], присваивая каждому указателю а[i] адрес вновь захватываемой памяти под массив из n чисел типа float.
При работе с динамически задаваемыми массивами начинающие часто забывают освобождать память, захваченную для массива. Память следует вновь возвращать в распоряжение операционной системы, то есть освобождать операцией delete. Правда, при завершении работы функции main автоматически уничтожаются все переменные, созданные в программе, и указатели сегментов памяти получают свои исходные значения. Однако при разработке сложных многомодульных комплексов программ следует помнить о том, что захваченная память повисает, становится недоступной операционной системе при выходе из области действия указателя, который ссылается на ее начало. Это может вызвать отказ в выделении новой памяти в каком-то другом программном модуле, если весь объем области heap будет исчерпан. Операция delete совместно с операцией new позволяет контролировать процесс последовательного захвата и высвобождения динамической памяти. Чтобы освободить память, захваченную для одной переменной d, например с помощью оператора
double *d=new double;
достаточно в конце функции или блока, где использовалась переменная d, записать:
delete d;
Если был размещен массив переменных, например
float *p = new float[200],
то следует освобождать память оператором delete [] р;. Здесь квадратные скобки указывают компилятору на то, что освобождать следует то количество ячеек, которое было захвачено последней операцией new в применении к указателю р. Явно указывать это число не нужно.
4.4.4. Массивы указателей.
Указатели делятся на две основные категории: указатели на переменные и указатели на функции. Обе категории содержат адреса памяти, но они имеют различные свойства и назначение. Для них также справедливы и различные правила использования. Указатели на функцию используются с целью вызова различных функций, удовлетворяющих определенному прототипу. Выполнение каких-то арифметических операций над указателем на функцию лишено смысла и не допускается в языке C++. Указатели на переменные, обычно на массивы, допускают произведение арифметических действий над собой с целью, например, пробега по массиву. Хотя указатели и содержат числа со свойствами целых без знака (unsigned int), тем не менее, для них существуют свои собственные правила и ограничения при выполнении операций присвоения, преобразования и адресной арифметики. Следующий пример демонстрирует использование указателя на функции. Объявление double (*pFunc)(double); определяет переменную pFunc, имеющую тип указателя на функции. Эта переменная способна указывать на любую функцию, которая возвращает вещественное число и требует одно вещественное число в качестве параметра. Обрамляющие скобки обязательны, так как без них выражение задает прототип функции, которая возвращает указатель на double и требует параметр типа double.
#include <stdio.h>
#include <conio.h>
#include <math.h>
void main()
{ // Использование указателя на функции doublе (*pFunc) (double); // Указатель unsigned c=l;
double y; while (с)
{ // Цикл проверки printf ("\n\tSelect a function (0 - to quit)\n" "\n 0. Quit" "\n 1. Sqrt" "\n 2. Sin" "\n 3. Tan\n\n\t"); с = getch () -' 0'; // Реакция пользователя
switch(c)
{
case 1: pFunc = sqrt; break;
case 2: pFunc = sin; break;
case 3: pFunc = tan; break;
case 0: break;
default: с =0;
}
if (c) {
у = pFunc(1); //Вызов с помощью указателя
printf ("\n y(1) = %f",y);
}
}
}
Обратите внимание на выражение getch()-'0', использованное для анализа символа, введенного пользователем. Вычитая код символа '0' (равный 48), мы преобразуем код введенного символа в целое число, равное расстоянию символа в таблице кодов от символа '0'. Таким образом, если была введена цифра, то выражение дает число, равное этой цифре. Если введена не цифра, то переменная unsigned с содержит некоторое недопустимое число, которое игнорируется, и цикл запроса ввода повторяется.
Тема 4.5. Разработка списковых структур данных в Visual C++.