- •2011 Декабрь
- •Выражения. Правила вычисления выражений.
- •Операции и выражения
- •Алгоритмы линейного типа. Операторы ввода-вывода. Генератор случайных чисел.
- •Алгоритмы с ветвлением. Условные выражения. Инструкция if (полная и неполная форма). Вложенные if-инструкции. Конструкция if-else-if.
- •Алгоритмы с ветвлением. Инструкция switch. Вложенные инструкции switch. Примеры в реальных задачах.
- •Алгоритмы циклического типа. Цикл с фиксированным числом повторений for. Параметр цикла. Инструкции break, continue. Бесконечный цикл. Примеры.
- •Цикл с предусловием while. Управление выполнением цикла. Примеры использования цикла. Зацикливание. Инструкция break.
- •Цикл с постусловием do. Управление выполнением цикла. Примеры использования. Зацикливание. Инструкция break.
- •Возможности стандартной библиотеки conio.H. Управление экраном в текстовом режиме. Управление движением объекта.
- •Управление выводом на экран Структура экрана в текстовом режиме
- •Установка нужного текстового режима
- •Текстовое окно и работа с ним
- •Установка цвета для текстовой информации
- •Ввод-вывод в текстовом режиме
- •Управление курсором в текстовом режиме
- •Перемещение изображения
- •Структурированные типы данных: одномерные массивы. Создание и заполнение массива информацией. Доступ к элементу массива. Основные операции с одномерным массивом.
- •Различные алгоритмы сортировок одномерного массива.
- •6.4. Сортировка массивов
- •6.4.1. Сортировка методом простого включения (вставки)
- •6.4.2. Сортировка методом простого выбора
- •6.4.3. Сортировка методом простого обмена
- •6.5. Поиск в отсортированном массиве
- •Указатели. Описание указателя. Операции над указателем. Указатель и одномерный массив. Доступ к элементам массива через указатель.
- •Функции. Назначение функций. Прототип, описание и вызов функции. Правила действия областей видимости функций. Передача параметров по значению и ссылке.
- •Структурированные типы данных: двумерные массивы. Создание и заполнение массива. Доступ к элементу массива. Основные операции с двумерным массивом.
- •Строки. Стандартные команды обработки строк. Обработки строк и текстов. Команды ввода-вывода строк.
- •17.Структуры. Создание и заполнение структур информацией. Доступ к полям структуры.
- •18.Графика. Возможности стандартной библиотеки graphics.H. Алгоритмы построения графических изображений.
- •19.Понятие и назначение файлов. Открытие файла, чтение и запись информации в файл, закрытие файла, проверка достижения конца файла.
- •21.Перегрузка функций. Встраиваемые функции.
- •22.Основные концепции ооп. Классы. Оператор разрешения области видимости. Объекты. Доступ к членам класса. Массивы объектов. Указатели на объекты.
- •23.Конструкторы и деструкторы. Параметризованные конструкторы. Встраиваемые функции в объявлении класса.
- •24.Дружественные функции. Присваивание объектов. Передача объектов функциям.
- •25.Понятие о наследовании. Управление доступом к членам базового класса. Использование защищенных членов.
25.Понятие о наследовании. Управление доступом к членам базового класса. Использование защищенных членов.
Наследование — один из трех фундаментальных принципов объектно-ориентированного программирования, поскольку именно благодаря ему возможно создание иерархических классификаций. Используя наследование, можно создать общий класс, который определяет характеристики, присущие множеству связанных элементов. Этот класс затем может быть унаследован другими, узкоспециализированными классами с добавлением в каждый из них своих, уникальных особенностей.
В стандартной терминологии языка C++ класс, который наследуется, называется базовым. Класс, который наследует базовый класс, называется производным. Производный класс можно использовать в качестве базового для другого производного класса. Таким путем и строится многоуровневая иерархия классов.
Понятие о наследовании
Базовый класс наследуется производным классом.
Язык C++ поддерживает механизм наследования, позволяя в объявление класса встраивать другой класс. Для этого базовый класс задается при объявлении производного. Лучше всего начать с примера. Рассмотрим класс auto, который в самых общих чертах определяет дорожное транспортное средство. Его члены данных позволяют хранить количество колес и число пассажиров, которое может перевозить транспортное средство.
class automob{ int kol; int pass;
public:
void set_kol(int num) { kol = num; }
int get_kol() { return kol; }
void set_pass(int num) { pass = num; }
int get_pass() { return pass; } };
Это общее определение дорожного транспортного средства можно использовать для определения конкретных типов транспортных средств. Например, в следующем фрагменте путем наследования класса auto создается класс grus (грузовых автомобилей).
class grus : public automob{int cargo;
public:
void set_cargo(int size) { cargo = size; }
int get_cargo() { return cargo; }
void show(); };
Тот факт, что класс grus наследует класс auto, означает, что класс grus наследует все содержимое класса auto. К содержимому класса auto класс grus добавляет член данных cargo, а также функции-члены, необходимые для поддержки члена cargo.
Обратите внимание на то, как наследуется класс auto. Общий формат для обеспечения наследования имеет следующий вид.
class имя_производного_класса : доступ имя__базового_класса {
тело нового класса }
Здесь элемент доступ необязателен. При необходимости он может быть выражен одним из спецификаторов доступа: public, private или protected. Подробнее об этих спецификаторах доступа вы узнаете ниже в этой главе. А пока в определениях всех наследуемых классов мы будем использовать спецификатор public. Это означает, что все public-члены базового класса также будут public-членами производного класса. Следовательно, в предыдущем примере члены класса grus имеют доступ к открытым функциям-членам класса auto, как будто они (эти функции) были объявлены в теле класса grus. Однако класс grus не имеет доступа к private-членам класса auto. Например, для класса grus закрыт доступ к члену данных kol.
Рассмотрим программу, которая использует механизм наследования для создания двух подклассов класса auto: grus и legk.
//Шилдт стр 353
// Демонстрация наследования,
#include <iostream.h>
#include <conio.h>
// Определяем базовый класс транспортных средств,
enum type {car, van, wagon};
class automob {int kol; int pass;
public:
void set_kol(int num) { kol = num; }
int get_kol() { return kol; }
void set_pass(int num) { pass = num; }
int get_pass() { return pass; }
};
// Определяем класс грузовиков,
class grus : public automob {int cargo;
public:
void set_cargo(int size) { cargo = size; }
int get_cargo() { return cargo; }
void show();
};
// Определяем класс автомобилей.
class legk : public automob {
enum type car_type;
public:
void set_type(type t) { car_type = t; }
enum type get_type() { return car_type; }
void show();
};
void grus::show(){
cout <<"Koles: "<<get_kol()<<"\n";
cout<<"Passajir: "<<get_pass()<<"\n";
cout<<"Grusopodjemnost: "<<cargo<<"\n"; }
void legk::show () {
cout<<"Koles: "<<get_kol()<<"\n";
cout<<"Passajir: "<<get_pass()<< "\n";
cout<<"Tip kusova: ";
switch(get_type()) {
case van: cout <<"legkovoi\n"; break;
case car: cout <<"sedan\n"; break;
case wagon: cout << "kabriolet\n"; }}
int main () {clrscr();
grus t1, t2;
legk c;
t1.set_kol(18); t1.set_pass(2) ; t1.set_cargo(3200);
t2.set_kol(6); t2.set_pass(3); t2.set_cargo(1200);
t1.show() ; cout<<"\n"; t2.show(); cout<<"\n";
c.set_kol(4); c.set_pass(6); c.set_type(van);
c.show();
getch(); return 0; }
При выполнении эта программа генерирует такие результаты.
колес: 18
пассажиров: 2
грузовместимость в кубических футах: 3200
колес: 6
пассажиров: 3
грузовместимость в кубических футах: 1200
колес: 4 пассажиров: 6 тип: автофургон
Как видно по результатам выполнения этой программы, основное достоинство наследования состоит в том, что оно позволяет создать базовый класс, который затем можно включить в состав более специализированных классов. Таким образом, каждый производный класс может служить определенной цели и при этом оставаться частью общей классификации.
И еще. Обратите внимание на то, что оба класса grus и legk включают функцию-член show (), которая отображает информацию об объекте. Эта функция демонстрирует еще один аспект объектно-ориентированного программирования — полиморфизм. Поскольку каждая функция show() связана с собственным классом, компилятор может легко "понять", какую именно функцию нужно вызвать для данного объекта.
После ознакомления с общей процедурой наследования одним классом другого можно перейти и к деталям этой темы.
Управление доступом к членам базового класса
Если один класс наследует другой, члены базового класса становятся членами производного. Статус доступа членов базового класса в производном классе определяется спецификатором доступа, используемым для наследования базового класса. Спецификатор доступа базового класса выражается одним из ключевых слов: public, private или protected. Если спецификатор доступа не указан, то по умолчанию используется спецификатор private, если речь идет о наследовании типа class. Если же наследуется тип struct, то при отсутствии явно заданного спецификатора доступа по умолчанию используется спецификатор public. Рассмотрим рамификацию (разветвление) использования спецификаторов public или private. (Спецификатор protected описан в следующем разделе.)
Если базовый класс наследуется как public-класс, его public-члены становятся public-членами производного класса.
Если базовый класс наследуется как public-класс, все его public-члены становятся public-членами производного класса. Во всех случаях private-члены базового класса остаются закрытыми в рамках этого класса и не доступны для членов производного. Например, в следующей программе public-члены класса base становятся public-членами класса derived. Следовательно, они будут доступны и для других частей программы.
//Шилдт стр 355
// Демонстрация наследования,
#include <iostream.h>
#include <conio.h>
class base { int i, j ;
public:
void set (int a, int b) { i = a; j = b; }
void show() { cout << i << " " << j << "\n"; } };
class derived:public base{int k;
public:
derived(int x) { k = x; }
void showk() { cout<<k<<"\n"; } };
int main() { clrscr();
derived ob(3);
ob.set(1, 2); // доступ к членам класса base
ob.show(); // доступ к членам класса base
ob.showk(); // доступ к члену класса derived
getch(); return 0; }
Поскольку функции set() и show() (члены класса base) унаследованы классом derived как public-члены, их можно вызывать для объекта типа derived в функции main(). Поскольку члены данных i и j определены как private-члены, они остаются закрытыми в рамках своего класса base.
Если базовый класс наследуется как private-класс, его public-члены становятся private-членами производного класса.
Противоположностью открытому (public) наследованию является закрытое (private). Если базовый класс наследуется как private-класс, все его public-члены становятся private-членами производного класса. Например, следующая программа не скомпилируется, поскольку обе функции set() и show() теперь стали private-членами класса derived, и поэтому их нельзя вызывать из функции main ().
// Эта программа не скомпилируется.
#include <iostream>
#include <conio.h>
using namespace std;
class base { int i, j;
public:
void set(int a, int b) { i = a; j = b; }
void show() { cout<<i<<" "<<j<<"\n"; } };
// Открытые члены класса base теперь становятся
// закрытыми членами класса derived,
class derived : private base { int k;
public:
derived(int x) { k = x; }
void showk() { cout<<k<<"\n"; }
int main() {clrscr();
derived ob (3);
ob.set(l, 2); // Ошибка, доступа к функции set() нет.
ob.show(); // Ошибка, доступа к функции show() нет.
Getch(); return 0; }
Важно запомнить, что в случае, если базовый класс наследуется как private-класс, его открытые члены становятся закрытыми (private) членами производного класса. Это означает, что они доступны для членов производного класса, но не доступны для других частей программы.
Использование защищенных членов
Член класса может быть объявлен не только открытым (public) или закрытым (private), но и защищенным (protected). Кроме того, базовый класс в целом может быть унаследован с использованием спецификатора protected. Ключевое слово protected добавлено в C++ для предоставления механизму наследования большей гибкости.
Если член класса объявлен с использованием спецификатора protected, он не будет доступен для других элементов программы, которые не являются членами данного класса. С одним важным исключением доступ к защищенному члену идентичен доступу к закрытому члену, т.е. к нему могут обращаться только другие члены того же класса. Единственное исключение из этого правила проявляется при наследовании защищенного члена. В этом случае защищенный член существенно отличается от закрытого.
Спецификатор доступа protected объявляет защищенные члены или обеспечивает наследование защищенного класса.
Как вы знаете, закрытый член базового класса не доступен никаким другим частям программы, включая и производные классы. Однако с защищенными членами все обстоит иначе. Если базовый класс наследуется как public-класс, защищенные члены базового класса становятся защищенными членами производного класса, т.е. доступными для производного класса. Следовательно, используя спецификатор protected, можно создать члены класса, которые закрыты в рамках своего класса, но которые может унаследовать производный класс, причем с получением доступа к ним.
Рассмотрим следующий пример программы.
//Шилдт стр 357
// Демонстрация наследования,
#include <iostream.h>
#include <conio.h>
class base { protected:
int i, j; // Эти члены закрыты в классе base,
// но доступны для класса derived,
public:
void set(int a, int b) { i = a; j = b; }
void show() { cout<<i<<" "<<j<<"\n"; } };
class derived : public base { int k;
public:
// Класс derived имеет доступ к членам класса base i и j.
void setk() { k = i*j; }
void showk() { cout<<k<<"\n"; } };
int main() {clrscr();
derived ob;
ob.set(2, 3); // OK, классу derived это позволено.
ob.show(); // OK, классу derived это позволено.
ob.setk() ; ob.showk() ;
getch(); return 0; }
Поскольку класс base унаследован классом derived открытым способом (т.е. как public-класс), и поскольку члены i и j объявлены защищенными в классе base, функция setk() (член класса derived) может получать к ним доступ. Если бы члены i и j были объявлены в классе base закрытыми, то класс derived не мог бы обращаться к ним, и эта программа не скомпилировалась бы.