- •Предисловие
- •Глава 1. Основные понятия
- •1.1. Элементы языка программирования
- •1.2. Процесс создания программы
- •1.3. Первая программа
- •1.4. Состав программы
- •Глава 2. Средства разработки на C++
- •2.1. Системы Turbo C++ 3.0/Borland C++ 3.1
- •2.2. Система C++ Builder
- •Глава 3. Работа с числовыми данными
- •3.1. Целые типы
- •3.2. Числа с плавающей точкой
- •3.3. Ввод и вывод чисел
- •3.4. Логический тип и логические операции
- •3.5. Математические функции
- •Глава 4. Операторы. Ключевые слова
- •4.1. Операторы
- •4.2. Приоритеты операторов
- •4.3. Ключевые слова
- •4.4. Структура программы
- •4.5. Константы
- •Задачи - . Простейшие вычисления
- •Глава 5. Управление и циклы
- •5.1. Условный оператор
- •5.2. Операторы цикла
- •5.3. Переключатель
- •5.4. Операторы break и continue
- •Задачи -. Выбор и циклы
- •Глава 6. Массивы
- •6.1. Одномерные массивы
- •6.2. Двумерные массивы
- •Задачи -. Одно- и двумерные массивы
- •Глава 7. Функции
- •7.1. Определение функции
- •7.2. Формальные параметры и фактические аргументы
- •7.3. Автоматические и статические переменные
- •7.4. Прототипы функций
- •7.5. Массивы как аргументы функций
- •7.6. Внешние переменные
- •7.7. Рекурсия
- •7.8. Перегруженные имена функций
- •7.9. Аргументы функций по умолчанию
- •Задачи -. Функции
- •Глава 8. Символы и строки
- •8.1. Символы
- •8.2. Строки символов
- •Задачи -. Символы и строки
- •Глава 9. Препроцессор
- •9.1. Директивы препроцессора
- •9.2. Макросы
- •Задачи -. Макросы
- •Глава 10. Указатели и ссылки
- •10.1. Указатели и адреса
- •10.2. Указатели и массивы
- •10.3. Адресная арифметика
- •10.4. Символьные указатели
- •10.5. Массивы указателей
- •10.6. Указатели на функции
- •10.7. Ссылки
- •10.8. Операторы new и delete
- •Задачи -. Указатели и ссылки
- •Глава 11. О файлах и командной строке
- •11.1. Знакомство с файлами
- •11.2. Командная строка
- •11.3. Перенаправление стандартного ввода и вывода на файл
- •11.4. Аргументы командной строки
- •Задачи -. Файлы и командная строка
- •Глава 12. Работа с экраном дисплея
- •12.1. Текстовый режим
- •12.2. Графический режим
- •Задачи -. Работа с экраном
- •Глава 13. Внутреннее представление чисел
- •13.1. Двоичная система счисления
- •13.2. Беззнаковые целые
- •13.3. Двоичный дополнительный код
- •13.4. Двоичный код с избытком
- •13.5. Побитовые операторы
- •13.6. Дробные числа в двоичной системе
- •13.7. Внутреннее представление плавающих типов
- •13.8. Преобразование типов
- •Задачи -. Побитовые операторы
- •Глава 14. Структуры, перечисления, объединения
- •14.1. Объявление структур
- •14.2. Структуры и функции
- •14.3. Указатели на структуры
- •14.4. Массивы структур
- •14.5. Перечисления
- •14.6. Объединения
- •14.7. Битовые поля
- •14.8. О бинарных файлах
- •Задачи -. Структуры
- •Глава 15. Классы
- •15.1. Структуры в C++. Инкапсуляция
- •15.2. Встроенные функции
- •15.3. Классы. Скрытие данных
- •15.4. Конструкторы
- •15.5. Статические члены класса
- •15.6. Друзья класса
- •15.7. Копирование объектов класса
- •15.8. Управление доступом
- •15.9. Ссылка на себя
- •15.10. Деструкторы
- •Задачи -. Работа с классами
- •Глава 16. Программы из нескольких файлов
- •16.1. Работа с проектами
- •16.2. Область действия имен
- •16.3. Заголовочные файлы
- •16.4. Пространства имен
- •Задачи -. Работа со стеком
- •Глава 17. Перегрузка операторов
- •17.1. Правила перегрузки операторов
- •Задачи -. Перегрузка операторов
- •Глава 18. Конструктор копирования и оператор присваивания
- •18.1. Проблемы при копировании
- •Задачи -. Конструктор копирования
- •Глава 19. Ввод и вывод
- •19.1. Вывод
- •19.2. Ввод
- •19.3. Ввод и вывод определяемых пользователем типов
- •19.4. Работа с файлами
- •Глава 20. Взаимоотношения классов
- •20.1. Объекты как члены класса
- •20.2. Конструкторы встроенных типов
- •20.3. Наследование
- •20.4. Виртуальные функции
- •20.5. Абстрактные классы
- •20.6. Совместимость типов
- •20.7. Множественное наследование
- •Задачи -. Наследование классов
- •Глава 21. Шаблоны, исключения
- •21.1. Шаблоны
- •21.2. Шаблоны функций
- •21.3. Классы и шаблоны
- •21.4. Обработка исключений
- •21.5. Стандартная библиотека шаблонов
- •Литература
- •Предметный указатель
|
Взаимоотношения классов 285 |
delete[] name; |
// удаление старого имени |
name = new char [strlen(ps.name) + 1]; // Память под новое имя |
|
strcpy(name, ps.name); |
// Копирование имени |
this->Date::operator=(ps); |
// копирование даты |
} |
|
return *this; |
// Возврат ссылки на объект |
}
В операторе присваивания дату, содержащуюся в объекте ps, нужно скопировать в данный объект. Для этого через указатель this явным образом вызывается функция-оператор присваивания класса Date, которой в качестве аргумента передается ps. Присваивание вида:
d = ps.d; m = ps.m; y = ps.y;
недопустимо, так как закрытые члены d, m, y класса Date недоступны для функций производного класса Pers, хотя и являются членами этого класса «по наследству». Присваивание дат можно произвести без использования this инструкцией:
Date :: operator=(ps);
так как функция, вызываемая в методе класса, «знает», что должна использовать данные текущего объекта.
Все остальное в файле PrsDeriv.cpp совпадает с файлом
Person.cpp.
Для испытания производного класса Pers можно использовать функцию man из файла MainPers.cpp, включив в него вместо директивы
#include "Person.h"
директиву
#include "PrsDeriv.h"
Созданная программа будет работать так же, как программа 58.
20.4.Виртуальные функции
Рассмотрим случай, когда в базовом и производном классах объявлены одинаковые функции (одинаковое имя, одинаковое количество и типы аргументов, один и тот же тип возвращаемого значения).
Программа 61. Невиртуальные функции
// Файл NoVirtF.cpp |
|
#include <iostream.h> |
|
struct base{ |
// Базовый класс base |
286 |
20 |
|
|
void fun(int i) |
// Функция базового класса |
||
{cout << "\n base::i= " << i;} |
|
|
|
}; |
|
|
|
struct derive: public base{ |
// Производный класс derive |
||
void fun(int i) |
// Функция производного класса |
||
{cout << "\n derive::i= " << i;} |
|
||
}; |
|
|
|
#include <conio.h> |
|
|
|
void main() |
|
|
|
{ |
|
|
|
base B, *pb = &B; |
// Объект base и указатель на base |
||
derive D, *pd = &D; |
// Объект derive и указатель на derive |
||
base* pbd = &D; |
// Указатель на base инициализируется |
||
|
|
// адресом объекта derive |
|
pb->fun(1); |
// Печатает |
base::i=1 |
|
pd->fun(5); |
// Печатает |
derive::i=5 |
|
pbd->fun(4); |
// Печатает |
base::i=4; |
|
getch(); |
|
|
|
} |
|
|
|
Указателю на базовый класс можно присвоить адрес объекта производного класса, т.к. в производном классе есть все члены, которые есть в базовом. Обратное невозможно, т.е. указателю на производный класс нельзя присвоить адрес объекта базового класса, т.к. в базовом классе, вообще говоря, нет всех компонент, которые есть в производном. Например:
base B; |
// Объект базового класса |
derive *pd; |
// Указатель на производный класс |
pd = &B; |
// Ошибка! Нельзя указателю на производный класс |
|
// присвоить адрес объекта базового класса |
Так как pbd есть указатель на базовый класс, при обращении pbd -> fun(4);
вызывается функция базового класса base::fun(), а не функция производного класса derive::fun(), несмотря на то, что указатель pbd имеет значение адреса объекта производного класса.
Выбор нужной функции устанавливается при компиляции программы в соответствии с типом указателя, а не с его значением, которое может быть разным в разные моменты работы программы. Такой режим называется ранним или статическим связыванием.
Позднее или динамическое связывание обеспечивают виртуальные
функции. Рассмотрим пример.
|
|
Взаимоотношения классов 287 |
|
Программа 62. Виртуальные функции |
|||
// Файл VirtualF.cpp |
|
|
|
#include <iostream.h> |
|
|
|
struct base{ |
|
// Базовый класс base |
|
virtual void vfun(int i) |
// Виртуальная функция базового класса |
||
{cout << "\n base::i= " << i;} |
|
||
}; |
|
|
|
struct derive1: public base{ |
// Производный класс derive1 |
||
void vfun(int i) |
|
// Переопределение виртуальной функции |
|
{cout << "\n derive1::i= " << i;} |
|
||
}; |
|
|
|
struct derive2: public base{ |
// Еще один производный класс derive2 |
||
void vfun(int i) |
|
// Переопределение виртуальной Функции |
|
{cout << "\n derive2::i= " << i;} |
|
||
}; |
|
|
|
#include <conio.h> |
|
|
|
void main() |
|
|
|
{ |
|
|
|
base B, *pb = &B; |
|
// Объект base и указатель на base |
|
derive1 D1, *pd1 = &D1; |
// Объект derive1 и указатель на derive1 |
||
derive2 D2, *pd2 = &D2; |
// Объект derive2 и указатель на derive2 |
||
pb->vfun(1); |
|
// Печатает |
base::i=1 |
pd1->vfun(2); |
|
// Печатает |
derive1::i=2 |
pd2->vfun(3); |
|
// Печатает |
derive2::i=3 |
pb = &D1; |
// Изменяем значение указателя на базовый класс |
||
pb->vfun(4); |
|
// Печатает |
derive1::i=4 |
pb = &D2; |
// Еще раз изменяем указатель на базовый класс |
||
pb->vfun(5); |
|
// Печатает |
derive2::i=5 |
getch(); |
|
|
|
} |
|
|
|
Интерпретация каждого вызова виртуальной функции через указатель на базовый класс зависит от значения указателя. В табл.60 указаны функции, вызываемые в зависимости от значения указателя.
Таблица 60. Вызовы виртуальной функции
Значение указателя pb Функция, вызываемая инструкцией pb->vfun
&B |
base::vfun() |
&D1 |
derive1::vfun() |
&D2 |
derive2::vfun() |
Так как при компиляции программы невозможно предвидеть, какое значение примет указатель на базовый класс при работе программы,
288 20
вопрос о выборе виртуальной функции решается на этапе выполнения программы и определяется значением указателя.
При вызове через указатель невиртуальной функции интерпретация определяется только типом, а не значением указателя.
Виртуальными могут быть только функции-члены класса.
В производном классе при объявлении виртуальной функции слово virtual можно не использовать, но лучше использовать, чтобы сделать программу более понятной.
20.5. Абстрактные классы
Абстрактным называется класс, в котором есть хотя бы одна чистая виртуальная функция. Чистой виртуальной функцией называется функция-член, которая имеет следующее определение:
virtual <ТИП><ИМЯ_ФУНКЦ>(<СПИСОК_ФОРМАЛЬН_ПАРАМ>) = 0;
Конструкция = 0 называется чистый спецификатор. Следующая функция является чистой виртуальной:
virtual void fpure(void) = 0;
Чистая виртуальная функция ничего не делает и недоступна для вызовов. Она служит основой для подменяющих ее функций в производных классах.
Абстрактный класс может быть использован только как базовый класс для производных классов. Нельзя создавать объекты абстрактных классов. Механизм абстрактных классов разработан для представления общих понятий, которые в дальнейшем предполагается конкретизировать. Эти общие понятия, обычно, невозможно использовать непосредственно. Например, можно говорить об общем понятии «фигура» на плоскости. Это понятие включает свойства, присущие любой конкретной фигуре: треугольнику, прямоугольнику, окружности. Например, у любой фигуры есть свойство цвет. Но изобразить можно всегда только конкретную фигуру, а не фигуру вообще.
Программа 63. Абстрактный класс фигур
В программе создан абстрактный класс Figure фигур на плоскости, который включает общие свойства, имеющиеся у любой фигуры: координаты центра, цвет, и общие для всех фигур методы: изображения на экране, стирания с экрана, перемещения по экрану. Функция рисования сделана чистой виртуальной. На базе абстрактного класса Figure определены конкретные классы эллипсов и прямоугольников. Для рисования фигур на экране используется графическая система TC.
|
|
Взаимоотношения классов 289 |
|
// Файл Figure.h |
|
|
|
#ifndef FIGUREH |
|
|
|
#define FIGUREH |
|
|
|
class Figure{ |
// Класс фигур на плоскости |
||
int x, y; |
// Координаты центральной точки фигуры |
||
int color; |
|
// Цвет фигуры |
|
public: |
|
|
|
int& GetX(){return x;} |
|
// Доступ к центральной |
|
int& GetY(){return y;} |
|
// точке |
|
int GetClr() {return color;} |
|
// Доступ к цвету |
|
Figure(int xx, int yy, int clr): x(xx), y(yy) |
// Конструктор |
||
{color = clr;} |
|
|
|
virtual void Show(int clr)=0; |
// Чистая виртуальная функция |
||
|
|
// рисования фигуры |
|
void Hide(); |
|
// Скрыть изображение |
|
void Move(int dx, int dy); |
|
// Переместить фигуру на dx и dy |
|
void Movement(); |
|
// Движение, пока не нажата ESC |
|
}; |
|
|
|
#endif |
|
|
|
// Файл Figure.CPP |
|
|
|
#include "Figure.h" |
|
|
|
#include<graphics.h> |
|
|
|
void Figure::Hide() |
|
// Убирает изображение фигуры |
|
{ |
|
|
|
int bk = getbkcolor(); |
|
// Цвет фона |
|
int cc = getcolor(); |
|
// Цвет рисования |
|
Show(bk); |
|
// Рисуем цветом фона (стираем) |
|
setcolor(cc); |
|
// Восстанавливаем цвет рисования |
|
} |
|
|
|
void Figure::Move(int dx, int dy)
{
Hide();
x+=dx; y+= dy; Show(color);
}
#include <conio.h>
void Figure::Movement() |
// Движение при нажатии клавиш |
|
{ |
// со стрелками, пока не нажата Esc |
|
const int UP = 72, DOWN = 80, |
|
// Константы |
RIGHT = 77, LEFT = 75, ESC = 27; |
/ /для кодов клавиш |
290 20
char c = 32; |
|
|
while(c != ESC){ |
|
// Пока не нажата клавиша Esc |
c = getch(); |
|
|
if(c == 0) { |
// Возможно нажата одна из клавиш-стрелок |
|
c = getch(); |
|
|
switch(c){ |
|
|
case LEFT: |
Move(-1, 0); |
break; |
case RIGHT: |
Move(1, 0); |
break; |
case UP: |
Move(0, -1); |
break; |
case DOWN: |
Move(0, 1); |
break; |
}// switch
}// if(c == 0)
}// while
}
// Файл Demoabst.cpp |
|
|
#include"Figure.h" |
|
|
#include<conio.h> |
|
|
#include<graphics.h> |
|
|
class Ellipse : public Figure{ |
// Класс эллипсов |
|
unsigned int a, b; |
// Полуоси эллипса |
|
public: |
|
|
Ellipse(int x0, int y0, int ai, int bi, int clr): |
// Конструктор |
|
Figure(x0, y0, clr), |
// Вызов конструктора базового класса |
|
a(ai), b(bi) |
// Вызов конструкторов членов класса |
|
{ } |
// Пустое тело конструктора класса Ellipse |
|
void Show(int clr) |
// Рисование эллипса |
|
{ |
|
|
int cc = getcolor(); |
// Запоминаем цвет рисования |
|
setcolor(clr); |
// Устанавливаем для рисования цвет фигуры |
|
ellipse(GetX(), GetY(), 0, 360, a, b); // Вызов функции для рисования |
||
setcolor(cc); |
// Восстанавливаем прежний цвет рисования |
|
} |
|
|
}; |
|
|
class Rect : public Figure{ |
// Класс прямоугольников со сторонами, |
|
|
// параллельными осям координат |
|
unsigned int a, b; |
// Стороны прямоугольника |
|
public: |
|
|
Rect(int x0, int y0, int ai, int bi, int clr): // Конструктор |
||
Figure(x0, y0, clr), |
// Вызов конструктора базового класса |
|
a(ai), b(bi) |
// Вызов конструкторов членов класса |
|
{} |
// Пустое тело конструктора класса Rect |
|
void Show(int clr) |
// Рисование прямоугольника |
|
Взаимоотношения классов 291 |
{ |
|
int cc = getcolor(); |
// Запоминаем цвет рисования |
setcolor(clr); |
// Устанавливаем для рисования цвет фигуры |
// Вызов функции для рисования
rectangle(GetX() - a / 2, GetY() - b / 2, GetX() + a / 2, GetY() + b / 2);
setcolor(cc); |
// Восстанавливаем прежний цвет рисования |
||
} |
|
|
|
}; |
|
|
|
#include<iostream.h> |
|
|
|
void main() |
|
|
|
{ |
|
|
|
Ellipse E(100, 200, 80, 50, YELLOW); |
// Создаем желтый эллипс |
||
Rect R(100, 200, 100, 50, BLUE); |
// Создаем синий прямоугольник |
||
int dr = DETECT, mod; |
|
|
|
// Инициализация графики |
|
|
|
initgraph(&dr, &mod, "D:\\Programs\\Tc30\\BGI"); |
|||
E.Show(E.GetClr()); getch(); |
// Вызов функции Ellipse::Show() |
||
R.Show(R.GetClr()); getch(); |
// Вызов функции Rect::Show() |
||
E.Movement(); getch(); |
|
// Вызов функции Figure::Movement() для |
|
|
|
// объекта E производного класса Ellipse |
|
R.Movement(); getch(); |
|
// Вызов функции Figure::Movement() для |
|
|
|
// объекта R производного класса Rect |
|
closegraph(); |
|
// Закрытие графического режима |
|
} |
|
|
|
Примерный вид картинки на экране показан на рис.79. Программа после создания изображений эллипса и прямоугольника ждет нажатия клавиш-стрелок и перемешает сначала эллипс. При нажатии клавиши Esc «активным» становится прямоугольник, который также можно подвигать.
Рис.79. Картинка на экране, создаваемая программой 63 Для рисования эллипса на экране использована функция
void ellipse(int x, int y, int stangle, int endangle, int xradius, int yradius);
которая рисует на экране дугу эллипса установленным цветом рисования. Здесь x, y – координаты центра эллипса, xradius,