
- •Приложение г Лекция 1. Элементы Языка си Используемые символы
- •Константы
- •Идентификаторы
- •Ключевые слова
- •Использование комментариев в тексте программы
- •Лекция 2. Типы данных и их объявление
- •Категории типов данных
- •Целый тип данных
- •Данные вещественного типа
- •Указатели
- •Операции разадресации и адреса
- •Переменные перечислимого типа
- •Лекция 3. Выражения и присваивания Операнды и операции
- •Преобразования при вычислении выражений
- •Операции отрицания и дополнения
- •Операция sizeof
- •Мультипликативные операции
- •Аддитивные операции
- •Операции сдвига
- •Поразрядные операции
- •Логические операции
- •Операция последовательного вычисления
- •Условная (тернарная) операция
- •Операции увеличения и уменьшения
- •Простое присваивание
- •Составное присваивание
- •Приоритеты операций и порядок вычислений
- •Побочные эффекты
- •Преобразование типов
- •Лекция 3. Операторы
- •Оператор выражение
- •Пустой оператор
- •Составной оператор
- •Оператор if
- •Оператор switch
- •Оператор break
- •Оператор while
- •Оператор do while
- •Оператор for
- •Сумма чисел от 1 до 100
- •Микрожизнь
- •Оператор continue
- •Оператор return
- •Оператор goto
- •Лекция 4. Массивы
- •Поиск минимума, сортировка
- •Ввод-вывод, обнуление
- •Двумерный массив
- •Лекция 5. Структуры
- •Объединения (смеси)
- •Поля битов
- •Переменные с изменяемой структурой
- •Определение объектов и типов
- •Лекция 6. Инициализация данных
- •Определение и вызов функций
- •Ссылки как псевдонимы переменных
- •Ссылки в качестве параметров функции
- •Ссылка в качестве возвращаемого значения
- •Передача массивов
- •Прототип
- •Указатели на функцию
- •Рекурсия
- •Предварительная инициализация параметров функции
- •Функции с переменным числом параметров
- •Передача параметров функции main
- •Исходные файлы и объявление переменных
- •Объявления функций
- •Время жизни и область видимости программных объектов
- •Лекция 7. Инициализация глобальных и локальных переменных
- •Методы доступа к элементам массивов
- •Указатели на многомерные массивы
- •Операции с указателями
- •Массивы указателей
- •Лекция 8. Динамические объекты
- •1. Выделение памяти в соответствие с типом указателя
- •2. Выделение памяти под нетипизированный указатель
- •Лекция 9. Динамическое создание и уничтожение массивов
- •Директивы Препроцессора
- •Директива #include
- •Директива #define
- •Директива #undef
- •Лекция 10. Условные директивы препроцессора
- •Линейный односвязный список
- •Лекция 11. Объектно-ориентированный подход к программированию
- •Ссылки на Себя
- •Инициализация
- •Копирующий конструктор
- •Очистка
- •Законченный Класс
- •Доступ к членам
- •Статические Члены
- •Лекция 12. Наследование
- •Перегрузка Операций
- •Операции Преобразования
- •Стандартный ввод/вывод
- •Форматируемый вывод
- •Манипуляторы
- •Ввод-вывод двоичных данных
- •Ввод/вывод с диска
- •Ввод/вывод для типов данных, определенных пользователем
- •Шаблоны функций
- •Шаблоны классов
- •Лекция 14. Библиотека stl
- •Итераторы
- •Алгоритмы
- •Контейнеры
- •Функциональные объекты
- •Пример. Работа с контейнером vector
- •Пример 2. Алгоритмы и функциональные объекты
- •Лекция 15. Обработка исключительных ситуаций
- •Лекция 16. Rtti и приведение типов
- •Операция typeid
Законченный Класс
Программирование без скрытия данных (с применением структур) требует меньшей продуманности, чем программирование со скрытием данных (с использованием классов). Структуру можно определить, не слишком задумываясь о том, как ее предполагается использовать. А когда определяется класс, все внимание сосредотачивается на обеспечении нового типа полным множеством операций; это важное смещение акцента. Время, потраченное на разработку нового типа, обычно многократно окупается при разработке и тестировании программы.
Вот пример законченного типа PascalSet, который реализует понятие «битовое множество целых однобайтовых беззнаковых»:
Файл pascalset.h
#ifndef _PS_H
#define _PS_H
#include <mem.h>
class PasSet {
unsigned char data[32];
unsigned char GetMask(unsigned char x)
{return 1<<x;}
unsigned char GetNumByte(unsigned char x)
{return x/8;}
unsigned char GetNumBit(unsigned char x)
{return x%8;}
public:
//Конструктор
PasSet()
{memset(data, 0, sizeof(data));}
//Проверка вхождения
int InSet(unsigned char x);
//Включение элемента
void include(unsigned char x);
//Исключение элемента
void exclude(unsigned char x);
//Вывод
void print();
};
#endif
Файл pascalset.cpp
include <stdio.h>
#include "pascalset.h"
int PasSet::InSet(unsigned char x)
{
unsigned char nbyte=GetNumByte(x);
unsigned char nbit=GetNumBit(x);
unsigned char mask=GetMask(nbit);
return data[nbyte] & mask;
}
void PasSet::include(unsigned char x)
{
unsigned char nbyte=GetNumByte(x);
unsigned char nbit=GetNumBit(x);
unsigned char mask=GetMask(nbit);
data[nbyte]|=mask;
}
void PasSet::exclude(unsigned char x)
{
unsigned char nbyte=GetNumByte(x);
unsigned char nbit=GetNumBit(x);
unsigned char mask=GetMask(nbit);
if (InSet(x)) data[nbyte]^=mask;
}
void PasSet::print()
{
for (int i=0; i<256; i++)
if (InSet((unsigned char)i)) printf("%d ", i);
printf("\n");
}
Файл mainset.cpp
#include <stdio.h>
#include "pascalset.h"
int main(int argc, char* argv[])
{
PasSet s;
s.include(8);s.include(3);s.include(5);
s.print();
s.exclude(3);
s.print();
if (s.InSet(5)) printf("5 is in set\n");
else printf("5 is not in set\n");
getchar();
return 0;
}
Результат работы
3 5 8
5 8
5 is in set
Доступ к членам
Сокрытие информации— это формальный механизм, предотвращающий прямой доступ к внутреннему представлению типа класса из функций программы. Ограничение доступа к членам задается с помощью секций тела класса, помеченных ключевыми словамиpublic,privateиprotected—спецификаторами доступа. Члены, объявленные в секцииpublic, называются открытыми, а объявленные в секцияхprivateиprotectedсоответственно закрытыми или защищенными.
открытый члендоступен из любого места программы. Класс, скрывающий информацию, оставляет открытыми только функции-члены, определяющие операции, с помощью которых внешняя программа может манипулировать его объектами;
закрытый члендоступен только функциям-членам идрузьямкласса. Класс, который хочет скрыть информацию, объявляет свои данные-члены закрытыми;
защищенный членведет себя как открытый по отношению кпроизводному классуи как закрытый по отношению к остальной части программы.
В теле класса может быть несколько секций public,protectedиprivate. Каждая секция продолжается либо до метки следующей секции, либо до закрывающей фигурной скобки. Если спецификатор доступа не указан, то секция, непосредственно следующая за открывающей скобкой, по умолчанию считаетсяprivate.
Друзья
Предположим, вы определили два класса, vector и matrix (вектор и матрица). Каждый скрывает свое представление и предоставляет полный набор действий для манипуляции объектами его типа. Теперь определим функцию, умножающую матрицу на вектор. Для простоты допустим, что в векторе четыре элемента, которые индексируются 0...3, и что матрица состоит из четырех векторов, индексированных 0...3. Допустим также, что доступ к элементам вектора осуществляется через функцию elem(), которая осуществляет проверку индекса, и что в matrix имеется аналогичная функция. Один подход состоит в определении глобальной функции multiply() (перемножить) примерно следующим образом:
vector multiply(matrix& m, vector& v);
{
vector r;
for (int i = 0; i<3; i++) { // r[i] = m[i] * v;
r.elem(i) = 0;
for (int j = 0; j<3; j++)
r.elem(i) += m.elem(i,j) * v.elem(j);
}
return r;
}
Это своего рода «естественный» способ, но он очень неэффективен. При каждом обращении к multiply() elem() будет вызываться 4*(1+4*3) раза. Теперь, если мы сделаем multiply() членом класса vector, мы сможем обойтись без проверки индексов при обращении к элементу вектора, а если мы сделаем multiply() членом класса matrix, то мы сможем обойтись без проверки индексов при обращении к элементу матрицы. Однако членом двух классов функция быть не может. Нам нужно средство языка, предоставляющее функции право доступа к закрытой части класса. Функция не член, получившая право доступа к закрытой части класса, называется другом класса (friend). Функция становится другом класса после описания как friend. Например:
class vector {
float v[4];
// ...
friend vector multiply(matrix&, vector&);
};
class matrix {
vector v[4];
// ...
friend vector multiply(matrix&, vector&);
};
Функция друг не имеет никаких особенностей, помимо права доступа к закрытой части класса. В частности, friend функция не имеет указателя this (если только она не является полноправным членом функцией). Описание friend — настоящее описание. Оно вводит имя функции в самой внешней области видимости программы и сопоставляется с другими описаниями этого имени. Описание друга может располагаться или в закрытой, или в открытой части описания класса; где именно, значения не имеет. Теперь можно написать функцию умножения, которая использует элементы векторов и матрицы непосредственно:
vector multiply(matrix& m, vector& v);
{
vector r;
for (int i = 0; i<3; i++) { // r[i] = m[i] * v;
r.v[i] = 0;
for (int j = 0; j<3; j++)
r.v[i] += m.v[i][j] * v.v[j];
}
return r;
}
Функция член одного класса может быть другом другого. Например:
class x {
// ...
void f();
};
class y {
// ...
friend void x::f();
};
Нет ничего необычного в том, что все функции члены одного класса являются друзьями другого. Для этого есть даже более краткая запись:
class x {
friend class y;
// ...
};
Такое описание friend делает все функции члены класса y друзьями x.