- •Раздел 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.3.2. Указатели и адресная арифметика.
Указатели являются очень мощным и гибким средством языка С. Корректное использование указателей позволяет создавать компактные и эффективные программы. Но потеря контроля на указателем может привести к очень неприятным последствиям. Наиболее часто в этом случае программа зависает (зацикливается в выполнении). Иногда портится область памяти, занятая операционной системой. В основном это происходит при работе в DOS.
Указатель — это адрес памяти. После инициализации он содержит адрес какой-либо переменной. Значение указателя показывает, где в памяти храните объект, а не что хранится по адресу. Операторный символ * используется для разадресации, то есть выборки содержимого по адресу. Существует обратная операция & взятия адреса. Механизм доступа к переменной с помощью указателя можно представить себе в виде перемещаемого по памяти окошка для просмотра или изменения ее содержимого.
Присвоение указателю адреса какой-либо другой переменной позволяет получить ее значение путем разадресации указателя (*р). Значения адресов зависят от операционной системы, версии компилятора и момента запуска в многозадачной системе. После выполнения оператора p=&v указатель р содержит адрес переменной v, который компилятор определил для хранения значений этой локальной переменной.
Две операции языка C++ (new и delete) позволяют непосредственно управлять временем жизни какой-либо переменной. Переменная может быть создана в любой точке программы с помощью операции new и затем при необходимости уничтожена с помощью операции delete. Пример иллюстрирует применение этих операций. Оператор р = new int; присваивает переменной р адрес начала области памяти длиной в один элемент типа int. Память выделяется динамически из специальной области центрального пула памяти, которая называется heap. Элемент занимает sizeof(int) байт. Операция sizeof(arg) возвращает размер в байтах своего аргумента arg, который может быть либо типом, либо выражением.
Операция delete освобождает ранее занятую память, возвращая ее в heap. Сам указатель при этом продолжает указывать на тот же участок памяти, но он больше ею не управляет. При освобождении памяти нет необходимости явно указывать ее размер, так как эту информацию сохраняет система при реализации операции new. Характерные значения (Oxcdcdcdcd и Oxdddddddd) определяются способом работы с динамической памятью, принятым в операционной системе (в данном случае Windows NT). Если программист забывает освободить память, то это остается незамеченным в небольших программах, однако часто приводит к отказам в достаточно серьезных разработках и является плохим стилем программирования.
Наряду с рассмотренными операциями в языках С и C++ существуют две функции для захвата памяти в области heap. Действия по выделению памяти для массива array (из size вещественных) выглядят следующим образом:
float *array; //Адрес начала будущего массива
array=(float*) malloc (sizeof(float)*size); //или
array=(float*) calloc (size, sizeof(float)):
Здесь malloc и callос — функции, прототипы которых объявлены в файле alloc.h. Функция malloc (Nbyte) запрашивает память размером Nbyte байт из области heap и возвращает указатель на первый байт этого участка. Возвращаемый указатель имеет описатель void*. Это означает, что тип переменной, на которую он может указывать, не определен. Так как мы собираемся присвоить его переменной array (указателю на тип float), то необходимо осуществить преобразование с помощью cast-преобразования (float*).
В языке C++ имеется возможность инициализировать указатель путем запроса памяти прямо в момент объявления переменной. Например,
float *р = new float[n]; //'Инициализация указателя р
Здесь действует некая условность. Эту же операцию можно было бы выполнить в два этапа.
float *р; // Объявление указателя р р = new float [n]; // Присвоение указателю р
Важно понять, что в сокращенной записи адрес вновь выделенного участка памяти будет присвоен указателю р, а не содержимому по адресу (*р), как это может показаться при рассмотрении примера. Опыт показывает, что такая трактовка сначала вызывает затруднения у начинающих, поэтому следует внимательно проанализировать обе формы и постараться запомнить принятую условность.
Указатели, объявленные вне функций или объявленные с описателем static, автоматически инициализируются нулями, как и все глобальные переменные в C++. Указатели, объявленные внутри функции (автоматические переменные), не инициализированы вовсе. Использование такого указателя до присвоения ему осмысленного значения является серьезной ошибкой. Нулевой адрес (NULL или 0) не может быть присвоен указателю никакой из функций или операций динамического выделения памяти (new, malloc и т. д.). В связи с этим он обычно служит признаком того, что указатель еще не был инициализирован программистом. NULL — это символическая константа, определенная в stdio.h для нулевого указателя. Символической константой называется константа, определенная с помощью макроподстановки #define. Например, #define NULL (void*)0 определяет символическую константу NULL, которая до компиляции везде будет заменена препроцессором на выражение (void*)0. Константа NULL определена как целый ноль, явно приведенный к типу void*, то есть указателю на произвольный, неопределенный тип.