
- •1. Объектно-ориентированное программирование как методология проектирования программных средств.
- •1.2. Объект
- •Iarray a(10), b(25), c(45);
- •Int main(void)
- •2.1. Перегрузка имен функций
- •Int print(int);
- •Void print(int);
- •Int (*matrix)[10];
- •Void main(void)
- •Void incr(int &aa)
- •3.2 Объявление класса.
- •Имя_класса имя объекта;
- •Имя объекта. Имя элемента
- •Имя_объекта. Имя_метода( );
- •Имя класса *this;
- •Имя_класса (список формальных параметров)
- •Листинг 3.4
- •V_3d( double &X, double &y,double &z)
- •Void main(void)
- •Х:: х(const х&)
- •V_3d::v_3d(const v_3d& v1)
- •Void v_3d::print()
- •Сlass X
- •Void main (void)
- •4. Перегрузка операций
- •V_3d(double x1,double y1,double z1)
- •Void print();
- •Void main(void)
- •Void v_3d::print()
- •4.3.1. Использование дружественных функций при перегрузке операций
- •Void print();
- •Void main(void)
- •Void v_3d::print()
- •4.3.2.Особенности использования дружественных функций
- •Имя объекта. Имя_функции
- •Void f1( X& );
- •Void f2 ( X* );
- •Void main(void)
- •5. Производные классы
- •Int Bfunc( );
- •Int Xfunc( );
- •Int Efunc( X & x1 );
- •Istream
- •Iostream
- •Ifstream
- •С помощью манипуляторов – специальных операций, вставляемых непосредственно в поток вывода.
- •Int width( int w);
- •Int precision( int p);
- •Void main(void)
- •Void open(char* filename, int mode, int access);
- •If(!fout) {
- •7. Виртуальные функции
- •Circle a;
- •A.Build();
- •Virtual double a(double X)
- •Void main (void)
- •Void Graphics Object :: Build() { };
- •8.Шаблоны функций и классов
- •Void main(void)
- •Void Swap ( SwapType &X, SwapType &y)
- •Void main(void)
- •Int a, double b;
- •Int sloc,rloc;//начало и конец очереди
- •Void qput(t I); // помещение элемента в очередь
- •Int main(void)
- •1.Какой принцип ооп реализуется с помощью шаблонных функций и классов?
- •2.Когда целесообразно использовать шаблоны функций? Каков общий вид объявления шаблона функции?
5. Производные классы
5.1. Наследование свойств как принцип ООП. Объявление производного класса
Язык С++ позволяет классу наследовать элементы данных и элементы-функции одного или нескольких других классов. Другими словами, новый класс может получить атрибуты и поведение от уже существующего класса.
Таким образом создается иерархия типов данных (классов). Имеющиеся классы –называют базовыми, а новые – производными (наследниками). Производные классы получают ,,наследство,, -данные и методы своих базовых классов и пополняются собственными данными и методами. В свою очередь, производный класс может служить базовым для другого класса.
Наследование позволяет абстрагировать некоторое общее или схожее поведение различных объектов в одном базовом классе.
Основная форма синтаксиса объявления производного класса:
сlass имя _производного _класса : < режим доступа >
имя _ базового _ класса
{
//............
};
Пример:
class A {
//…
};
class X: public A
{
//…
};
Здесь A – базовый класс, X- производный.
5.2. Режимы доступа при наследовании
Режим определяет уровень доступа к элементам базового класса внутри производного. При объявлении производного класса он не является обязательным и может быть privaite, public, protected.
Если режим доступа опущен, то по умолчанию это privaite. В таблице 5.1 описаны основные режима доступа при наследовании.
Таблица 5.1.Режимы доступа при наследовании.
-
Режим доступа в базовом классе.
Спецификатор доступа.
Доступ в производном классе.
protected
public
public
protected
public
private
public
Не доступны
protected
public
private
private
protected
public
protected
protected
protected
private
protected
Не доступны
private
private
Не доступны
Примечание: private элементы базового класса остаются для него собственными и не доступны в производном классе.
Пример:
// Базовый класс
сlass B{
int a;
pubic:
int b,c;
Int Bfunc( );
};
// Производный класс.
сlass X: public B{
int d;
public:
int e;
Int Xfunc( );
};
// Внешняя функция
Int Efunc( X & x1 );
Xfunc( ) имеет доступ :
к b,c Bfunc() – наследуются как public – компоненты;
к d,e, Xfunc() – собственные компоненты;
Efunc()имеет доступ :
из класса B: к b,c, Bfunc() как public – компонентам;
из класса X: к e, Xfunc()
Даже если открытые члены базового класса становятся закрытыми при наследовании с использованием спецификатора private, они по-прежнему доступны внутри производного класса. Рассмотрим пример программы из листинга 5.1.
Листинг 5.1.
// Пример наследования со спецификатором private
#include <iostream.h>
// базовый класс
class base {
int x;
public:
void setx(int n) { x = n; }
void showx() { cout << "x="<< x << '\n'; }
};
// Наследование через private
class derived : private base {
int y;
public:
// setx доступна внутри derived
void setxy(int n, int m) { setx(n); y = m; }
// showx доступна внутри derived
void showxy() { showx(); cout <<"y="<< y << '\n'; }
};
main()
{
derived ob;
ob.setxy(10, 20);
ob.showxy();
return 0;
}
Результаты работы этой программы:
x=10
y=20
В данном случае функции showx() и setx() доступны внутри производного класса, так как они наследуется из базового и становятся закрытыми членами производного класса.
5.3. Инициализация наследуемых членов
Все действия по инициализации наследуемых членов выполняются конструктором порожденного класса. Для инициализации наследуемых данных в списке инициализации в заголовке определения конструктора производного класса должен быть указан конструктор базового класса.
Если конструктору базового класса должны быть переданы аргументы, они должны быть заданы в списке аргументов конструктора производного класса. Таким образом, список аргументов конструктора порожденного класса состоит из аргументов для конструктора порожденного класса и аргументов для конструктора базового класса.
Основная форма определения конструктора производного класса:
конструктор_производного_класса (список аргументов) : базовый класс1 (список аргументов),........, базовый класс N (список аргументов)
{...
// тело конструктора...........
};
Конструкторы базовых классов вызываются перед конструированием любых элементов производных классов.
Рассмотрим пример программы, показанной в листинге 5.2. В этой программе реализовано простое наследование, когда у производного класса имеется только один базовый.
Листинг 5.2. Простое наследование.
#include <iostream.h>
#include <stdlib.h>
#include <time.h>
// Базовый класс является моделью массива и содержит элементы-
// данные,описывающие свойства массива любого типа
//
class basearray
{
protected:
int maxitems; //максимальное число элементов
int citems; //число элементов, размещенных в массиве
public:
//конструктор
basearray(int nitems)
{
maxitems=nitems;citems=0;};
};
// производный класс в качестве элемента-данного содержит
// указатель на массив элементов конкретного типа
class iarray:public basearray
{
int *items; //указатель на массив
public:
//конструктор
iarray(int nitems);
//деструктор
~iarray();
//занесение элемента в массив
int putitem(int item);
//получение элемента из массива
int getitem(int ind,int &item);
//получение фактического числа элементов в массиве
int count() {return citems;};
};
//определение конструктора производного класса
// перед определением тела конструктора производного класса
// присутствует вызов конструктора базового класса
// : обеспечивает передачу параметров конструктору
// базового класса
iarray::iarray(int nitems):basearray(nitems)
{
items=new int[nitems];
}
//деструктор
iarray::~iarray()
{
delete items;
}
//занесение элемента в массив
int iarray::putitem(int item)
{
if(
citems<maxitems){
items[citems]=item;
citems++;
return 0;
}
else
return -1;
}
//получение элемента из массива
int iarray::getitem(int ind,int & item)
{
if(ind>=0 && ind<citems){
item=items[ind];
return 0;
}
else
return -1;
}
void main(void)
{
iarray m(100); //создан объект производного класса
int i,j,k;
int n;
randomize();
for(i=0;i<10;i++)
m.putitem(rand());
n = m.count();
for(i=0;i<n;i++){
m.getitem(i,j);
cout<<"индекс = "<<i<<" элемент = "<<j<<"\n";
}
}
Результаты работы этой программы:
индекс = 0 элемент = 27926
индекс = 1 элемент = 28543
индекс = 2 элемент = 22385
индекс = 3 элемент = 29846
индекс = 4 элемент = 27573
индекс = 5 элемент = 21881
индекс = 6 элемент = 23107
индекс = 7 элемент = 30123
индекс = 8 элемент = 28465
индекс = 9 элемент = 12165
5.4. Множественное наследование
Производный класс может иметь не один, а множество базовых классов. Это называется множественным наследованием. Когда наследуется множество базовых классов, конструкторы выполняются слева направо в порядке, задаваемом в объявлении производного класса. Деструкторы выполняются в обратном порядке.
Пример. Рассмотрим схему наследования, при которой производный класс прямо наследует два базовых :
BASE1 BASE 2
TOP
Class Base1
{
int x;
public:
Base1(int i) {x = i ; };
};
Class Base2
{
int x;
public:
Base2(int i): x(i){ };
};
Class Top : public Base1, public Base2
{
int a, b;
public:
Top(int i, int j) : Base1(i*5), Base2(j+i), a(i) {b = j; }
};
Здесь символ :, стоящий в определении производного класса перед именем конструктора базового класса обеспечивает механизм передачи параметров конструктору базового класса.
Создадим объект производного класса .
Top Z(1, 2);
При таком создании объекта Z производного класса вначале будет вызван конструктор первого базового класса Base1, что приведет к инициализации его собственного элемента-данного x значением 5, затем конструктор второго базового класса Base2, его собственный элемент данное x получит значение 3, и, наконец, будет вызван конструктор производного класса Top и его элементы данные получат значения а=1,b=2.
Пример законченной программы, иллюстрирующий множественное наследование, показан в листинге 5.3
Листинг 5.3. Множественное наследование.
#include <iostream.h>
class B1 {
public:
B1() { cout << "Работа конструктора B1\n"; }
~B1() { cout << "Работа деструктора B1\n"; }
};
class B2 {
int b;
public:
B2() { cout << "Работа конструктора B2\n"; }
~B2() { cout << "Работа деструктора B2\n"; }
};
// Наследование двух базовых классов.
class D : public B1, public B2 {
public:
D() { cout << "Работа конструктора D\n"; }
~D() { cout << "Работа деструктора D\n"; }
};
main()
{
D ob;
return 0;
}
Результаты работы этой программы:
Работа конструктора B1
Работа конструктора B2
Работа конструктора D
Работа деструктора D
Работа деструктора B2
Работа деструктора B1
Из примера видно, что базовые классы инициализируются в последовательности их объявления, затем происходит инициализация производных классов тоже в последовательности их объявления. Деструкторы вызываются в обратном порядке.
Важно:
Конструкторы базового класса должны быть объявлены с атрибутами public или protected для того, чтобы обеспечить возможность их вызова из производного класса.
Контрольные вопросы
1.В чем сущность механизма наследования как принципа ООП?
2.Как объявляется производный класс?
3.Как осуществляется доступ к элементам базового класса из производного при наследовании?
4.Каким образом выполняется инициализация наследуемых членов?
5.Как выполняются конструкторы базовых классов при множественном наследовании?
6.СИСТЕМА ВВОДА –ВЫВОДА С++
6.1. Базовые положения системы ввода-вывода С++
С++ поддерживает все функции ввода-вывода языка С и, кроме того, определяет свою собственную объектно-ориентированную систему ввода-вывода Система ввода-вывода С++, так же, как система ввода-вывода С, действует через потоки.
Поток – это логическое устройство, которое выдает и принимает информацию.
Когда программа на С++ начинает выполняться, автоматически открываются четыре потока, показанные в таблице 6.1.
Таблица 6.1. Стандартные потоки С++.
-
Поток
Значение
Устройство по умолчанию
cin
Стандартный ввод
Клавиатура
cout
Стандартный вывод
Экран
cerr
Стандартная ошибка
Экран
clog
Буферизуемая версия cerr
Экран
По умолчанию, стандартные потоки используются для связи с клавиатурой и экраном. Однако, в среде, в которой поддерживается переопределение ввода-вывода, эти потоки могут быть перенаправлены на другие устройства.
Система ввода-вывода С++ поддерживается заголовочным файлом iostream.h. В этом файле задана иерархия классов.
Библиотека потоковых классов построена на основе двух базовых классов: ios и streambuf. Класс streambuf обеспечивает буферизацию данных во всех производных классах, которыми пользуется программист. Обращаться к его методам из прикладных программ обычно не нужно. Класс streambuf обеспечивает взаимодействие создаваемых потоков с физическими устройствами. Класс ios и производные классы содержат указатель на класс streambuf. Методы и данные класса streambuf программист явно не использует. Этот класс нужен другим классам библиотеки ввода-вывода.
Упрощенная схема иерархии потоковых классов показана на рис.6.1.
ios