Лекция 6
Объектный подход. Воспоминания
Классы в С++. Структура класса
Классы в С++. Зоны видимости полей и методов
Классы в С++. Другие варианты объявления класса
Классы в С++. Другие варианты объявления класса
Классы в С++. Другие варианты объявления класса
Классы в С++. Дружественные классы
Классы в С++. Дружественные классы
Классы в С++. Дружественные классы
Классы в С++. Дружественные классы
Классы в С++. Дружественные классы
Классы в С++. Определение метода вне класса
Классы в С++. Указатель this
Классы в С++. Указатель this
Классы в С++. Указатель this
Классы в С++. Конструкторы классов
Классы в С++. Конструкторы классов. Пример
Классы в С++. Конструкторы классов. Пример
Классы в С++. Конструкторы классов
Классы в С++. Деструкторы классов
Классы в С++. Статические элементы класса
Классы в С++. Статические элементы класса
Классы в С++. Производные классы
Наследование. Доступ к полям и методам
Наследование. Доступ к полям и методам
Еще раз о множественном наследовании
Виртуальные классы
Виртуальные функции
Виртуальные функции
Виртуальные функции
Абстрактные классы
Абстрактные классы
Литература
1.37M
Category: programmingprogramming

Реализация объектной модели в С++ (лекция 6)

1. Лекция 6

Реализация объектной
модели в С++
1

2. Объектный подход. Воспоминания

Объектно-ориентированный подход в
программировании заключается в представлении
программы в виде совокупности автономно
действующих лиц (объектов), каждый из которых
является экземпляром определенного
типа(класса), а классы образуют иерархию с
наследованием свойств.
Элементы объектной модели:
абстрагирование;
инкапсуляция;
модульность;
иерархия;
типизация;
параллелизм;
сохраняемость.
2

3. Классы в С++. Структура класса

class class_name
{
int data_member; // поля
void show_member(int); // методы
};
Пример:
class employee
{ char name[64] ;
long employee_id;
float salary;
void show_employee(void)
{ cout << "Имя: " << name << endl;
cout << "Номер служащего: " << employee_id << endl;
cout << "Оклад: " << salary << endl;
};
};
3

4. Классы в С++. Зоны видимости полей и методов

class имя
{
private:

protected:

public:

};
4
Атрибут private имеют поля и методы класса, доступные только для
составных и дружественных функций этого класса. Эти поля и методы
класса называются закрытыми.
Атрибут protected имеют поля и методы класса, доступные для
составных и дружественных функций классов, которые являются
производными от этого класса или совпадают с ним. Эти поля и методы
класса называются защищенными.
Атрибут public имеют поля и методы класса, обращение к которым
осуществляется как к полям структуры. Эти поля и методы называются
открытыми.

5. Классы в С++. Другие варианты объявления класса

Вместо слова class в объявлении класса как нового типа
данных допустимо использовать слова:
Struct class_name
{
int data_member; // поля
void show_member(int); // методы
};
Или
Union class_name
{
int data_member; // поля
void show_member(int); // методы
};
5

6. Классы в С++. Другие варианты объявления класса

Если первичный класс объявлен с ключевым словом class, то
первые его члены будут закрытыми по умолчанию, если как struct,
то открытыми. В случае union члены класса могут быть только
открытыми.
Например,
class Vector
{
double x, y;
public:
double getx() {return x;}
double gety() {return y;}
};
то элементы класса x и y будут закрыты по умолчанию, и
обращение к ним, как к открытым членам, приведет к ошибке. Эти
элементы можно будет читать с помощью функции getx() и gety()
6

7. Классы в С++. Другие варианты объявления класса

class Vector
{
double x,y;
public:
double getx() {return x;}
double gety() {return y;}
};
void main()
{
Vector a; // объявление объекта типа Vector
int z;
z=a.x; //ошибка!
z=a.getx(); //верно
7
}

8. Классы в С++. Дружественные классы

Произвольная внешняя функция, прототип которой объявлен в теле
класса и имеет модификатор friend, называется дружественной
функцией этого класса.
Функция, не являющаяся методом класса, может иметь доступ к его
частным методам в случае, если она объявлена другом (friend)
класса. Например, в следующем примере функция frd() объявлена
другом класса cl (при этом реализована она может быть где угодно):
class cl {
...
public:
friend void frd();
...
};
Одна из причин, почему язык С++ допускает существование
функций-друзей, связана с той ситуацией, когда два класса должны
использовать одну и ту же функцию.
8

9. Классы в С++. Дружественные классы

9
Иначе говоря, дружественная функция — это функция, которая не
является членом класса, но имеет доступ к членам класса, объявленным
в полях private или protected.
Дружественная функция может располагаться в любом поле класса –
private, public или protected. Она при любых обстоятельствах будет иметь
доступ к private-элементам класса и, даже если она сама находится в
поле private (как в нашем примере), к ней можно будет обратиться вне
класса, не используя специальных методов.
При определении дружественной функции, элементы класса необходимо
явно передавать в нее в виде параметров функции. Так как она не
является компонентом класса, она не получает указатель this.
В виде параметра, в дружественную функцию так же надо передать
указатель или ссылку на объект класса. Иначе она не увидит, данные
какого класса ей принять и обработать.
Функция может использоваться, как дружественная к нескольким
классам.
Вызываются дружественные функции, как обычные функции – не
используется способ — Объект_класса.функция(). После внесения всех
необходимых параметров в нее при вызове, она сама увидит с
элементами какого класса и объекта надо работать.

10. Классы в С++. Дружественные классы

#include <iostream>
using namespace std;
class Point
{
friend void ChangePrivate( Point & );
public:
Point( void ) : m_i(0) {}
void PrintPrivate( void ){cout << m_i << endl; }
private:
int m_i;
};
void ChangePrivate ( Point &i ) { i.m_i++; }
10
int main()
{ Point sPoint;
sPoint.PrintPrivate();
ChangePrivate(sPoint);
sPoint.PrintPrivate(); }

11. Классы в С++. Дружественные классы

11
Функции-члены класса могут быть объявлены в других классах
как дружественные.
class A {
public:
int Func1( B& b );
private:
int Func2( B& b );
};
class B {
private:
int _b;
// A::Func1 is a friend function to class B
// so A::Func1 has access to all members of B
friend int A::Func1( B& );
};
int A::Func1( B& b ) { return b._b; } // OK
int A::Func2( B& b ) { return b._b; } // Error

12. Классы в С++. Дружественные классы

12
Класс friend – это класс, все функций-члены которого являются
дружественными функциями класса, то есть функции-члены которого
имеют доступ к закрытым и защищенным членам другого класса.
Предположим, что в классе friend было следующее объявление B:
friend class A;
В этом случае все функции-члены из класса A имели бы
дружественный доступ к классу B.В следующем коде приведен
пример дружественного класса.
Дружественные отношения не являются взаимными, если это не
указано явным образом.
Управляемый тип не может иметь дружественные функции, классы и
интерфейсы.
Дружественные отношения не наследуются; это означает, что
классы, производные от A, не могут обращаться к закрытым членам
класса B.
Дружественные отношения не являются переходящими, поэтому
классы, дружественные классу B, не могут обращаться к закрытым
членам класса A.

13. Классы в С++. Определение метода вне класса

class employee
{ public:
char name[64];
long employee_id;
float salary;
void show_employee(void); |————————> Прототип функции
};
Так как разные классы могут использовать функции с одинаковыми
именами, нужно предварять имена определяемых вне класса функций
именем класса и оператором глобального разрешения (::). В данном
случае определение функции становится следующим:
void employee:: show_employee (void)
{ сout << "Имя: " << name << endl;
cout << "Номер служащего: " << employee_id << endl;
cout << "Оклад: " << salary << endl;
};
13

14. Классы в С++. Указатель this

Указатель this доступен только в нестатических функциях-членах
типа class, struct или union. Он указывает на объект, для
которого вызывается функция-член. Статические функции-члены
не имеют указатель this.
this
this->member-identifier
14
Указатель this объекта не является частью самого объекта и не
отражается в результате оператора sizeof объекта. Вместо этого
при вызове нестатической функции-члена для объекта
компилятор передает адрес объекта в функцию в качестве
скрытого аргумента. Например, при вызове следующей функции:
myDate.setMonth( 3 );
можно интерпретировать следующим образом:
setMonth( &myDate, 3 );
Адрес объекта доступен из функции-члена как указатель this. В
большинстве случаев указатель this используется неявно.

15. Классы в С++. Указатель this

15
Явное использование указателя this при ссылке на члены класса
допустимо, хотя не рекомендуется. Например:
void Date::setMonth( int mn )
{
month = mn;
// These three statements
this->month = mn;
// are equivalent
(*this).month = mn;
}
Выражение *this обычно используется для возврата текущего
объекта из функции-члена.
return *this;
Указатель this также используется для защиты от рекурсивной
ссылки.
if (&Object != this) { // do not execute in cases of selfreference

16. Классы в С++. Указатель this

16
Явное использование указателя this при ссылке на члены класса
допустимо, хотя не рекомендуется. Например:
void Date::setMonth( int mn )
{
month = mn;
// These three statements
this->month = mn;
// are equivalent
(*this).month = mn;
}
Выражение *this обычно используется для возврата текущего
объекта из функции-члена.
return *this;
Указатель this также используется для защиты от рекурсивной
ссылки.
if (&Object != this) { // do not execute in cases of selfreference

17. Классы в С++. Конструкторы классов

Для заполнения полей объекта при его создании и для
освобождения полей при его удалении в Си++ предусмотрено
специальное средство, которое состоит из составных
функций, называемых конструктором и деструктором.
Класс может иметь несколько конструкторов. Имя каждого
конструктора совпадает с именем класса, для которого этот
конструктор определен. Конструкторы могут быть
перегружены и иметь параметры по умолчанию.
Конструктор не имеет возвращаемого значения.
Конструктор может быть объявлен и в закрытой, и в
защищенной, и в открытой части класса. Если конструктор
объявлен в закрытой части, то объекты этого класса могут
создаваться только дружественными функциями, а если в
защищенной – дружественными функциями производного
класса.
17
Конструктор может быть определен вне тела класса.

18. Классы в С++. Конструкторы классов. Пример

Определим класс двумерного вектора. Будем инициализировать его
с помощью полярных координат:
#include <iostream.h>
#include <math.h>
class Vector
{ double x, y;
public:
Vector( double rho, double phi);
void show() { cout << "Вектор = ("<< x << ", " << y << ")\n"; }
};
Vector::Vector(double rho, double phi = 0)
{
x = rho*cos(phi);
y = rho*sin(phi);
18
}

19. Классы в С++. Конструкторы классов. Пример

void main()
{
Vector v(1), w(-1, 0.5);
v.show(); w.show();
getch();
}
Результаты работы программы
Вектор = (1, 0)
Вектор = (-0.877583, -0.479426)
Обращение к конструктору осуществляется одним из трех
способов:
имя объект(параметры);
имя объект = имя(параметры);
имя объект = параметр;
где имя обозначает имя класса. Второй способ называется явным,
19
третий – сокращенным.

20. Классы в С++. Конструкторы классов

Конструктор имеет дополнительное средство для инициализации
полей. Это средство называется списком инициализации.
Список инициализации находится в заголовке определения
конструктора и отделяется от прототипа конструктора
двоеточием. Он состоит из слов поле(выражение),
инициирующих арифметические выражения над аргументами.
Например:
class Arr // массив чисел с плавающей точкой
{
int n; // максимальное число элементов
double *p; // указатель на массив
public:
Arr(int size, double *a): n(size), p(a) {}
};
В данном примере список инициализации означает то же самое, что
и присваивания n = size и p = a.
Преимущество списка инициализации заключается в том, что он
позволяет
задавать начальные значения констант и псевдонимов
20
(ссылок).

21. Классы в С++. Деструкторы классов

Деструктором называется составная функция класса, которая
вызывается перед разрушением объекта. Это означает, что
деструктор вызывается в следующих случаях:
Правила определения деструкторов:
21
при выходе из области видимости;
при выполнении операции delete для объектов, размещенных в
динамической памяти;
непосредственно, как составная функция.
класс имеет ровно один деструктор;
имя деструктора совпадает с именем класса с добавленным впереди
символом тильды «~»;
деструктор не имеет аргументов и не имеет возвращаемого значения.
Если же деструктор не определить явно, то он будет определен
по умолчанию, как составная функция
~ имя_класса() {};
и будет находиться в открытой части класса.

22. Классы в С++. Статические элементы класса

22
Определяя класс в тексте программы, мы создаем новый
тип данных, состоящий из полей и составных функций,
объединенных в структуру.
Объектом класса называется переменная этого типа.
Объект отличается от класса тем, что он занимает
некоторую область памяти, тогда как класс описывает
структуру полей, составляющих эту область.
Поля двух различных объектов класса не связаны между
собой.
Чтобы получить область, которая является общей для всех
объектов класса, достаточно описать поля этой области с
атрибутом static:
Описанные таким образом поля называются
статическими. Более точно, статическими называются
элементы класса, которые являются общими для всех
объектов этого класса.

23. Классы в С++. Статические элементы класса

#include <iostream>
using namespace std;
class www
{ public: static float f;
};
// Инициализация f:
float www::f = 0.5;
int main()
{ www a, b;
cout << www::f << ", memory address: " << &www::f<< endl;
cout << a.f << ", memory address: " << &www::f << endl;
cout << b.f << ", memory address: " << &www::f<< endl;
system("pause");
return 0;
}
23

24. Классы в С++. Производные классы

24
Класс, полученный из другого класса добавлением новых полей и
составных функций, называется производным классом.
Базовым называется класс, из которого получается производный
класс. Производный класс может быть сформирован на основе
нескольких базовых классов. В этом случае говорят о
множественном наследовании.
При наследовании важную роль играет статус доступа к
компонентам класса.
Формат определения производного класса:
class имя : {private или public} имя_1, имя_2, …, имя_N
{
дополнительные поля и функции производного класса
}
При создании объекта производного класса сначала вызываются
конструкторы базовых классов, а потом – конструктор
производного. Деструкторы вызываются в обратном порядке.

25. Наследование. Доступ к полям и методам

Функция или поле базового
класса могут быть
определены заново в
производном классе. Такой
член базового класса
называется
переопределенным.
При обращении к
переопределенной функции
будет вызвана ближайшая по
иерархии функция. Если в
этом случае требуется вызов
функции из базового класса
или вызов внешней функции,
то применяется оператор
разрешения области
видимости ::
25
#include <iostream.h>
void f()
{ cout << “\nВнешняя функция” }
struct Base1 { void f() { cout << “\nФункция
из Base1”;} };
struct Base2 { void f() { cout << “\nФункция
из Base2;”} };
struct Deriv: Base1, Base2
{
void f() {::f();} }
int main()
{ Deriv obj;
f();
obj.Base1::f();
obj.Base2::f();
obj.f();
return 0;
}
Внешняя функция
Функция из Base1
Функция из Base2
Внешняя функция

26. Наследование. Доступ к полям и методам

26
При наследовании статус доступа к полям и собственным
функциям базового класса изменяется следующим
образом: если перед базовым классом указан атрибут
private, то:
открытые члены базового класса переходят в
закрытые в производном классе,
защищенные – в закрытые,
закрытые – в недоступные.
Если перед базовым классом используется атрибут public,
то:
открытые члены базового класса переходят в
открытые в производном классе,
защищенные – в защищенные,
закрытые – в недоступные.

27. Еще раз о множественном наследовании

Доступ к членам базовых классов, имеющих одинаковые имена,
осуществляется через имена базовых классов, которым они
принадлежат, при помощи операции разрешения доступа.
Например:
Class A
{ public: void f(); };
class B
{ public: void f(); };
class C : public A, public B {};
void main()
{ C cc;
cc.f();
// ошибка – неизвестно, какая из функций вызывается A::f() или
B::f()
cc.A::f(); // правильный вызов
27
}

28. Виртуальные классы

Базовый класс называется виртуальным, если его поля не
дублируются при неоднократном наследовании. Виртуальный
базовый класс объявляется при наследовании при определении
производного класса следующим образом:
сlass имя_производного_класса:
virtual public имя_виртуального_базового_класса
{
тело производного класса;
}
Для того чтобы поля
четырехугольника не rect
наследовались более
одного раза, удобно
объявить его как
виртуальный базовый
28
класс.
четырехугольник
прямоугольник
four
ромб
квадрат
square
Рис. 4.3. Иерархия подклассов четырехугольника
romb

29. Виртуальные функции

Полиморфизмом в объектно-ориентированном
программировании называется способность объекта
отреагировать на некоторый запрос в ходе выполнения
программы.
Поскольку объект реагирует на запросы с помощью своих
составных функций, то эта способность реализуется на основе
механизма, позволяющего выбирать вызываемые функции не на
шаге компиляции программы, а на шаге ее выполнения.
Генерация вызова составной функции на шаге компиляции
называется ранним связыванием, а на шаге выполнения –
поздним связыванием.
Функция, имя которой связывается с соответствующим ей кодом
на стадии позднего связывания, называется виртуальной. В
языке Си++ полиморфизм реализован на основе виртуальных
функций.
Если в базовом классе определена составная функция, которая
должна различным образом выполняться для объектов различных
производных классов, то она в этих производных классах должна
29
быть определена заново. Такая функция называется
переопределенной.

30. Виртуальные функции

#include <iostream.h>
#include <conio.h>
class fruit
// Класс фрукты
{ public: void show() {cout << "фрукты"<< endl;}
};
class apple // Класс яблоки
{public: void show() {cout << "яблоки" << endl;}
};
class orange// Класс апельсины
{public: void show() {cout << "апельсины" << endl;} };
void main()
{
// Создаём объекты
fruit *a = (fruit *)new apple, *b = (fruit *)new orange;
a -> show(); b -> show(); // Выводим сообщения
getch(); // Ожидание нажатия клавиши
}
В результате работы программы будет два раза выведено слово
«фрукты», ибо оба оператора a->show() и b->show() вызовут функцию
30
show() из базового класса.

31. Виртуальные функции

31
Для того чтобы решить проблему переопределения функций в
производных классах, объекты которых заданы с помощью
указателей на объекты базовых классов, применяются
виртуальные функции.
Они определяются в базовом классе следующим образом:
virtual тип_возвращаемого_значения
имя(параметры)
Виртуальные составные функции позволяют выбирать члены
класса с одним и тем же именем через указатель функции в
зависимости от типа указателя.
В частности, если в примере в базовом классе указать
virtual void show(),
а остальной текст оставить без изменения, то программа выведет
слова «яблоки» и «апельсины».

32. Абстрактные классы

32
Функция, объявленная, но не определенная в базовом
классе, называется чисто виртуальной. Чисто
виртуальная функция должна быть переопределена в
каком-нибудь из производных классов. В базовом
классе она объявляется с помощью оператора
virtual тип_возвр_значения имя(параметры) = 0;
Например,
Class Figure
{
int color;
public:
virtual void show();
};

33. Абстрактные классы

33
Класс, содержащий хотя бы одну чисто виртуальную функцию,
называется абстрактным классом. Абстрактный класс может
служить только в качестве базового для других классов, ибо
объект такого класса создать невозможно. В производных от него
классах чисто виртуальные функции должны быть либо
переопределены, либо вновь указаны как абстрактные.
Абстрактный класс нельзя указать в качестве типа аргумента или
возвращаемого значения функции. Однако разрешено (и это часто
используется) создавать указатель на абстрактный базовый
класс.
Составные функции абстрактного класса могут вызывать чисто
виртуальные составные функции этого же класса. Абстрактный
класс может иметь конструкторы и деструкторы. Деструктор
базового класса вызывается при разрушении объекта, после того,
как все подобъекты этого объекта уже разрушены. Поэтому
деструктор базового класса не должен вызывать чисто
виртуальные функции своего класса, так как такой вызов приведет
к ошибке.

34. Литература

Белецкий Я. Турбо Си++: Новая разработка. – М.:
Машиностроение, 1994. – 400 с.
Намиот Д.Е. Язык программирования TURBO C++: Учеб.
пособие / Под ред. В.А. Сухомлинова. – М.: МГУ, 1991. – 121
с.
Рассохин Д. От Си к Си++. – М.: Издательство «ЭДЕЛЬ»,
1993. – 128 с.
Подбельский В.В. Язык Си++. – М.: Финансы и статистика,
2002. – 560 с.
Страуструп Б. Язык программирования Си++. – Киев:
«ДиаСофт», 1993. Ч. 1. – 264 с. Ч. 2. – 296 с.
Топп У., Форд У. Структуры данных в С++. – М.: ЗАО
«Издательство БИНОМ», 1999. – 816 с.
Шилдт Г. Теория и практика С++. – СПб.: BHV – СанктПетербург, 1996. – 416 с.
34
English     Русский Rules