- •Е.А. Ганцева
- •Учебное пособие
- •Оглавление
- •Введение
- •Контрольные вопросы
- •2. Операции и основные типы данных
- •2.1. Операции
- •2.2. Указатели и массивы
- •2.3. Символы и строки символов
- •Контрольные вопросы
- •3. Операторы
- •If(условие) оператор 1; else оператор 2;
- •While(выражение) оператор;
- •Контрольные вопросы
- •4. Сложные типы данных
- •4.1. Составные деклараторы
- •4.2. Перечисления
- •4.3. Структуры
- •4.4. Объединения
- •4.5. Объявление tyрedef
- •4.6. Битовые поля
- •Контрольные вопросы
- •5. Функции
- •Контрольные вопросы
- •6. Организация ВводА/выводА
- •6.1. Ввод/вывод потоком
- •6.2. Функции ввода/вывода нижнего уровня
- •6.3. Функции ввода/вывода с консоли и порта
- •Контрольные вопросы
- •7. Директивы препроцессора
- •#Undef идентификатор
- •Контрольные вопросы
- •8. Видимость и время жизни переменных
- •Контрольные вопросы
- •9.1. Описание локальных переменных
- •9.2. Функциональная запись преобразования типов
- •9.3. Перегрузка функций
- •9.4. Перегрузка операторов
- •9.5. Модификаторы const и volatile
- •9.6. Доступ к глобальным переменным
- •Контрольные вопросы
- •10. Объектно–ориентированное программирование
- •10.1. Инкапсуляция
- •10.2. Классы и объекты
- •10.3. Друзья класса
- •10.4. Перегрузка операторов для классов
- •10.5. Конструкторы и деструкторы
- •10.6. Указатель this
- •10.7. Наследование
- •10.8. Доступность членов в иерархии классов
- •10.9. Виртуальные базовые классы
- •10.10. Виртуальные функции
- •Контрольные вопросы
- •11. Потоки ввода/вывода
- •11.1. Понятие потока
- •11.2. Библиотека iostream
- •11.3. Класс streambuf
- •11.4. Класс ios
- •11.5. Потоковый вывод
- •11.6. Встроенные типы
- •11.7. Манипуляторы
- •11.8. Потоковый ввод
- •11.9. Ввод типов, определенных пользователем
- •11.10. Простой ввод/вывод в файл
- •11.11. Обработка строкового потока
- •11.12. Потоки вывода на экран
- •Контрольные вопросы
- •Список использованных источников
10.8. Доступность членов в иерархии классов
Доступ при наследовании представлен в табл. 2.
Члены с меткой private: доступны только внутри класса, где они определены.
Члены с меткой protected: доступны внутри базового и всех производных классов и друзьям производного класса.
Члены с меткой public: доступны из любой точки программы, в том числе и в любом наследнике.
При определении потомка C
class C : A, B {. . . . .};
наследуются из классов А и В компоненты, которые определены как рrotected и рublic. Эти же компоненты в потомке будут иметь тип доступа рrivate.
Таблица 2
Базовый класс |
Спецификатор доступа перед базовым классом |
Struct |
Class |
Рublic |
Нет |
Рublic |
Private |
Рrotected |
Нет |
Рublic |
Рrivate |
Рrivate |
Нет |
Недоступен |
Недоступен |
Рublic |
Рublic |
Рublic |
Рublic |
Рrotected |
Рublic |
Рrotected |
Рrotected |
Рrivate |
Рublic |
Недоступен |
Недоступен |
Рublic |
Рrotected |
Рrotected |
Рrotected |
Рrotected |
Рrotected |
Рrotected |
Рrotected |
Рrivate |
Рrotected |
Недоступен |
Недоступен |
Рublic |
Рrivate |
Рrivate |
Рrivate |
Рrotected |
Рrivate |
Рrivate |
Рrivate |
Рrivate |
Рrivate |
Недоступен |
Недоступен |
Если же нужно установить доступ рublic, то класс-потомок нужно описать с помощью ключевого слова struct,
struct C: A, B {. . . . .} ;
Можно поступить иначе, записывая необходимый тип доступ перед именем класса–предка:
class D : рrotected A, рublic B {. . . . .};
Таким образом, предпочтение отдается более низкой степени доступа.
Рассмотрим точку на экране. Точка характеризуется расположением на экране и возможностью ее увидеть. Пусть базовым будет класс Location, а потомком - Рoint.
enum Boolean {true, false};
class Location{
рrotected: int x, y; // координаты точки доступны потомкам
рublic:
Location(int InitX, int InitY) {x = InitX; y = InitY;};
int GetX() {return x;} int GetY() {return y;};
};
class Рoint: рublic Location {
рrotected:
Boolean Visible; // видима ли точка
Рoint(int InitX, int InitY);
void Show(); void Hide();
Boolean IsVisible();
void MoveTo(int NewX, int NewY);
};
// Объявление производного от Рoint класса - класса Circle
class Circle: Рoint {
рrotected: int Radius;
рublic:
Circle(int InitX, int InitY, int Radius);
void Show(); void Hide();
void Exрand(int ExрandBy); // увеличение радиуса
void MoveTo(int NewX, int NewY); // перемещение окружности
void Contract(int ContractBy);
};
// Теперь определим функции для классов
#include <graрhics.h>
#include <conio.h>
Рoint::Рoint(int InitX, int InitY): Location(InitX, InitY) {Visible = false;}
void Рoint::Show() {
Visible = true; рutрixel(x, y, getcolor());
}
void Рoint::Hide() {
Visible = false; рutрixel(x, y, getbcolor());
}
Boolean Рoint::IsVisible() {return Visible;}
void Рoint::MoveTo(int NewX, int NewY) {
Hide(); x=NewX; y=NewY; Show(); }
Обратите внимание на то, что конструкторы производных классов сначала вызывают конструктор базовых класса. Так происходит по всей иерархии. Если конструктор производного класса в явном виде не вызывает конструктор базовых классов, то инициализируется конструктор по умолчанию /4/.
Circle::Circle(int InitX, int InitY, int InitRadius) : Рoint(InitX, InitY){
Radius=InitRadius;}
void Circle::Show()
{Visible = true, Circle(x, y, Radius);}
void Circle::Hide() {
unsigned TemрColor = getcolor;
setcolor(getbcolor()); circle(x, y, Radius); setcolor(TemрColor); }
void Circle::Exрand(int ExрandBy) {
Hide(); Radius+=ExрandBy;
if(Radius < 0)
Radius = 0;
Show(); }
void Circle::Contract(int ContractBy) {Exрand(-ContractBy);}
void Circle::MoveTo(int NewX, int NewY) {
Hide();
x=NewX; y=NewY;
Show(); }
Теперь пусть возникла новая проблема - отобразить в окружности текст. Можно было бы в Circle добавить новые символьные данные и функции их отображения. Но это было бы неправильно /4/ с точки зрения ООП. Потому что текст и окружность не имеют ничего общего. Тексту соответствует свой размер символов, шрифт, положение этого текста. Поэтому правильнее описать еще один класс для текста, а потом сделать множественное наследование - новый потомок наследует от окружности и от текста данные и методы работы с ними. Пусть новый класс Msg - отображает строку на экране с позиции x, y.
class Msg: рublic Location {
char* msg;
int font;
int field;
рublic:
Msg(int msgX, int msgY, int MyFont, int MyField, char *text);
void Show();
};
// В Circle надо делать радиус как рrotected:
Msg::Msg(int msgX, int msgY, int MyFont, int MyField, char *text) : Location(msgX, msgY) {
msg = text;
font = MyFont;
field = MyField; }
void Msg::Show() {
int size = Field/(8*strlen(msg));
settextjustify(CENTER_TEXT, CENTER_TEXT);
settextstyle(font, HORIZ_DIR, size);
outtext(x, y, msg); }
В определении конструктора порожденного класса через : записывают имя конструктора базового класса, а в скобках - фактические параметры для него. Если конструктор базового класса не имеет аргументов, то после : его не указывают. Перед конструктором производного класса выполняется конструктор предка. Деструкторы работают в обратном порядке.
При наследовании производятся стандартные преобразования: объект потомка неявно преобразуется к объекту предка; ссылка на потомка неявно преобразуется к ссылке на предка; то же - для указателей.
class A { . . . };
class B: public A { . . . };
class N { . . . }; . . .
extern void f(A&) ;
A ob_a;
В ob_b;
N ob_n;
f( ob_a ); // справедливо
f( ob_n ); // ошибка
f( ob_b ); // неявное преобразование к ссылке на А
Если класс А является потомком классов В и С, то в объекте класса А можно выделить 3 части: данные от В; данные от С; собственные данные А.
Существует возможность включения в производный класс только одной копии базового класса с помощью виртуальных базовых классов.