Слайд 2Основные идеи ООП
Абстракция
Инкапсуляция
Наследование
Полиморфизм и виртуальные функции
Позднее связывание
Слайд 3Абстракция данных
Абстракция позволяет выделить существенные характеристики некоторого
объекта, отличающие его
от всех других видов объектов
Высокоуровневые обращения к объекту могут обрабатываться с помощью вызова методов низкого уровня
Слайд 4Инкапсуляция
Инкапсуляция - способность объекта скрывать внутреннее
устройство своих свойств и методов
Согласно данному
принципу, класс должен рассматриваться как черный ящик
Внешний пользователь не знает детали реализации объекта и работает с ним только путем предоставленного объектом интерфейса
Следование данному принципу может уменьшить число связей между классами и упростить их независимую реализацию, модификацию и тестирование
Слайд 5class Rectangle : public Shape
{
public:
double GetArea();
double GetWidth();
double GetHeight();
private:
// здесь располагаются
зарытые поля и методы
// необходимые для работы с прямоугольником
};
Пример. Прямоугольник.
Слайд 6Наследования
Наследование позволяет описать новый класс на основе уже существующего
родительского (базового) класса
Класс-потомок может добавить свои собственные свойства и методы,
пользоваться методами и свойствами базового класса
Наследование позволяет строить иерархии классов
Слайд 7Пример с фигурами
class Shape
{
public:
virtual double GetArea();
};
class Rectangle
: public Shape
{
public:
virtual double GetArea();
private:
double width;
double height;
};
class Circle : public Shape
{
public:
virtual double GetArea()
private:
double radius;
};
Слайд 8Полиморфизм
Полиморфизм даёт возможность использовать
одинаковые имена
(прототипы) для функций,
исполняющих разный код
Полиморфизм
позволяет обрабатывать объекты
классов-потомков как однотипные объекты, не смотря
на то, что реализация методов у них может различаться
Слайд 9Пример с фигурами.
class Shape
{
public:
virtual double GetArea();
};
class Rectangle
: public Shape
{
public:
virtual double GetArea()
{
return width * height;
}
private:
double width;
double height;
};
class Circle : public Shape
{
public:
virtual double GetArea()
{
return 3.14*radius*radius;
}
private:
double radius;
};
Слайд 10Позднее связывание.
Позднее (динамическое) связывание заключается в
разрешении ссылки на функцию во время исполнения,
а не на
этапе компиляции, когда компилятор не знает
из какого класса будет браться вызываемый метод.
Раннее (статическое) связывание осуществляется
на этапе компиляции, когда компилятор точно знает
какой метод и из какого класса будет вызван.
Слайд 11 Достоинства ООП.
Локализация кода и данных
улучшает наглядность и
удобство сопровождения программного обеспечения
Возможность
создания расширяемых систем.
Расширяемость означает, что существующую систему
можно заставить работать с новыми компонентами,
причем без внесения в нее каких-либо изменений.
Компоненты могут быть добавлены на этапе
исполнения программы
Слайд 12Недостатки ООП.
Документирование классов - задача более трудная,
чем это было в случае процедур и модулей.
Неэффективность на этапе выполнения
Излишняя универсальность
Слайд 13Классы и объекты.
В языке C++ абстрактным
типом данных, определяемым
пользователем является класс. Он вмещает в себя
поля и
методы для их обработки
Объект класса это переменная этого типа.
Другое её название – экземпляр класса.
Например
Класс – книга.
Объект – «Полный справочник по C++». Герберт Шилдт.
Слайд 14Объявление класса
class
{
[ private:]
полей и методов>
public:
полей и методов>
[ protected:]
<описание защищённых полей и методов>
} [список объектов];
Слайд 15Пример объявления класса
class Rectangle
{
float Width;
float Height;
public:
int GetArea();
void SetWidth(float Value)
{
Width = Value;
}
void SetHeight(int);
}
Слайд 16Поля класса
(переменные класса, данные класса)
Поля класса
хранят всю необходимую информацию об объекте. Изменение состояния объекта класса
связано с изменением его полей.
Поля класса могут иметь любой тип, кроме типа этого же класса (но могут быть указателями или ссылками на этот же класс).
При описании полей их инициализация не допускается.
Слайд 17Методы класса
Класс может содержать один или более методов,
позволяющих осуществлять манипуляцию с полями объекта
Метод объекта –
программный код, выполненный в виде процедуры или функции, реагирующий на передачу объекту определенного сообщения.
Вызов метода объекта может приводить к изменению его состояния (значение полей-данных), а может и не приводить.
Слайд 18Описание методов класса
Тип
возвращаемого имя_класса::имя_метода(список аргументов)
значения
{
// тело метода
}
Реализация методов
класса может быть вынесена за пределы объявления класса. В этом
случае описание метода выглядит следующим образом:
Наример
float Rectangle::GetArea()
{
return Width * Height;
}
Слайд 19Работа с элементами класса
Для того, что бы
вызвать открытый метод класса или получить доступ к его открытым
полям, необходимо указать имя объекта, за которым ставится оператор «.» и имя элемента класса, к которому мы получаем доступ. (Если идёт обращение через указатель, то вместо «.» используется «->»)
Например:
int S1,S2;
Rectangle LRect;
Rectangle *RRect = new Rectangle;
LRect.SetWidth(1);
LRect.SetHeight(2);
RRect->SetWidth(3);
RRect->SetHeight(4);
S1 = LRect.GetArea();
S2 = RRect->GetArea();
Слайд 20Описание объектов класса
Rectangle R1; // объект класса «прямоугольник»
Rectangle
R2(1,2); // объект класса «прямоугольник»
// с начальной инициализацией
// переменных класса
Rectangle R3[10]; // массив объектов класса
// «прямоугольник»
Слайд 21Хранение полей и методов класса
Объект 1
Объект 3
Объект 2
Слайд 22 Доступ к элементам класса
Доступ к
полям и методам класса извне может быть ограничен.
Для разделения прав доступа к полям и методам класса используются ключевые слова
public (открытые)
private (закрытые)
protected (защищённые)
Слайд 23Публичные (public) элементы класса
Public-методы и поля класса
определяют его интерфейс
доступ к ним возможен из любой части
кода
необходимо помещать в public-раздел класса только необходимый набор методов, выполняющих высокоуровневые операции над объектом класса
Слайд 24Закрытые (private) элементы класса
Private-данные и методы класса определяют его реализацию
Доступ
к ним разрешен только из методов данного класса
Рекомендуется все данные
класса делать закрытыми, их обработку осуществлять внутри методов
Закрытые методы класса обычно используются публичными методами, решая внутренние задачи класса
Слайд 25Защищенные(protected) элементы класса
Protected-поля и методы определяют интерфейс для производных классов
Доступ
к ним разрешен изнутри методов данного класса и всех его
потомков
В защищенной зоне размещают методы, которые не должны быть видны снаружи класса, но реализация которых может быть переопределена или использована производными классами
Слайд 26 Задача 1
int main()
{
int x = 1;
for( int i = 0; i < 10 ;
++i );
// Следующая строка - инкремент???????????/
++x;
printf(" X = %d \n", x);
return 0;
}
Слайд 27 Задача 2
int main()
{
int x = 1;
for( int i = 0; i < 10 ;
++i )
// Следующая строка - инкремент???????????/
++x;
printf(" X = %d \n", x);
return 0;
}
Слайд 28 Задача 3
int main()
{
int x = 1;
for( int i = 0; i < 10 ;
++i )
// Следующая строка - инкремент???????????/
++x;
printf(" X = %d \n", x);
return 0;
}
Слайд 29Найдите ошибки:
int &a;
int **mas = new int[3][4];
int *p = new
int(10);
if(p == 10)
p = 11;
Что будет выведено на
экран:
char* a = "ABCDEFGHJKLMNOP";
cout << *((char*)((int*)(a)+2)-3);
Прочитайте:
double**(*(*ch[3])(int t)[15])(char c);
Слайд 30class A
{
private int p;
};
class C
{
};
Будут ли конструкторы
у этих классов?
Слайд 31class A
{
public int * p1;
public int
val;
};
A a = new A();
int *mas = new int[10];
a->p1 =
mas;
a->val = 5;
A b = new A();
b = a;
delete a;
delete b;
delete[] mas;
Слайд 32Void*
В C++ существует специальный тип указателя, который называется указателем
на неопределённый тип. Для определения такого указателя вместо имени типа
используется ключевое слово void в сочетании с описателем, перед которым располагается символ *.
void *UndefPoint;
Для объекта типа указатель на объект неопределённого типа отсутствует информация о размерах и внутренней структуре адресуемого участка памяти. Из-за этого не могут быть определены какие-либо операции для преобразования значений.
UndefPoint = 0xb8000000; // Такое присвоение недопустимо
UndefPoint++; // Для типа void * нет такой операции…
Слайд 33Void*
Объектам типа указатель на объект неопределённого типа в качестве значений
разрешается присваивать значения лишь в сочетании с операцией явного преобразования
типа.
В этом случае указатель на объект неопределённого типа становится обычным указателем на объект какого-либо конкретного типа.
int mmm = 10;
pUndefPointer = (int *)&mmm;
pUndefPointer выступает в роли указателя на объект типа int.
(*(int *)pUndefPointer)++;
Слайд 34Void*
Для указателя на объект неопределённого типа не существует способа непосредственной
перенастройки указателя на следующий объект с помощью операции инкрементации. В
операторе, реализующем операции инкрементации и декрементации, только с помощью операций явного преобразования типа можно сообщить транслятору величину, на которую требуется изменить первоначальное значение указателя.
pUndefPointer++; // Это неверно, инкрементация не определена… (int *)pUndefPointer++; // И так тоже ничего не получается…
((int *)pUndefPointer)++; // Ok
++(int *)pUndefPointer; // Ok
Слайд 35Void*
pUndefPointer = (int *)pUndefPointer + sizeof(int);
Теперь перенастроили указатель на следующий
объект типа int.
pUndefPointer = (int *)pUndefPointer + 1;
Тот
же результат
Можно проводить достаточно нетривиальные операции
(*(char *)pUndefPointer)++;
Слайд 36Указатель на функцию
Каждая функция характеризуется типом возвращаемого значения, именем, количеством,
порядком следования и типами параметров.
При использовании имени функции без последующих
скобок и параметров имя функции выступает в качестве указателя на эту функцию, и его значением служит адрес размещения функции в памяти. Это значение адреса может быть присвоено другому указателю, и затем уже этот новый указатель можно применять для вызова функции. Однако в определении нового указателя должен быть тот же тип, что и возвращаемое функцией значение, то же количество, порядок следования и типы параметров. Указатель на функцию определяется следующим образом:
тип функции (*имя_указателя)(спецификация_параметров);
Слайд 37Указатель на функцию
int(*func1ptr) (char); — определение указателя func1ptr на функцию с
параметром типаchar, возвращающую значение типа int.
Если приведенную синтаксическую конструкцию записать без
первых круглых скобок, то есть в виде
int *fun(char);
то компилятор воспримет ее как прототип некой функции с именем fun и параметром типа char, возвращающей значение указателя типа int *.
char * (*func2Ptr) (char *, int); - определение указателя func2Ptr на функцию с параметрами типа указатель на char и типа int, возвращающую значение типа указатель на char.
Слайд 38// описание функции f1
void f1(void)
{
cout
"\n Выполняется f1()";
}
// описание функции f2
void f2(void)
{
cout << "\n Выполняется f2()";
}
void main()
{
// объявление указателя на функцию
void (*ptr)(void); // ptr - указатель на функцию
ptr = f2; // ptr инициализируется адресом f2()
(*ptr)(); // вызов f2() по ее адресу
ptr = f1; // присваивается адрес f1()
(*ptr)(); // вызов f1() по ее адресу
ptr(); // вызов эквивалентен (*ptr)();
}
Слайд 39int add (int n, int m) { return n +
m;}
int div (int n, int m) { return n %
m;}
int mult (int n, int m) { return n * m;}
int subt (int n, int m) { return n - m;}
void main()
{
int (*par)(int, int); // указательна функцию
switch(c)
{
case '+':
par = add; // par получает адрес функции add
break;
case '-':
par = subt; // par получает адрес функции subt
break;
…
}
cout << (*par)(a,b); /* вызов функции по адресу,
}
Слайд 40void func1(int);
void func2(int);
void func3(int);
void main()
{
void (*f[3])(int) =
{func1, func2, func3};
cout
1 и 3“;
cin >> choice;
while(choice >= 1 && choice < 4)
{
(*f[choice-1])(choice); // вызов функции f[choice]
cout << "Введите число между 1 и 3“;
cin >> choice;
}
}
Массивы указателей на функции
Слайд 41Конструкторы
Инициализацию состояния объекта в момент его создания
осуществляет специальная функция – конструктор
1) Конструктор имеет то же имя
что и класс.
2) Конструктор не возвращает значение, даже типа void. Нельзя получить указатель на конструктор.
3) Класс может иметь несколько конструкторов с разными параметрами для разных способов инициализации объекта (при этом используется механизм перегрузки).
4) Конструктор, вызываемый без параметров, называется конструктором по умолчанию.
5) Параметры конструктора могут иметь любой тип, кроме этого же класса. Можно задавать значения параметров по умолчанию. Их может содержать только один из конструкторов.
Слайд 42Конструкторы
6) Если в классе не указано ни одного конструктора, компилятор
создает его автоматически. Такой конструктор вызывает конструкторы по умолчанию для
полей класса и конструкторы по умолчанию базовых классов (занятее про «Наследование»). В случае, когда класс содержит константы или ссылки, при попытке создания объекта класса будет выдана ошибка, поскольку их необходимо инициализировать конкретными значениями, а конструктор по умолчанию этого делать не умеет.
7) Конструкторы не наследуются.
8) Конструкторы нельзя описывать с модификаторами const, virtual и static.
9) Конструктор вызывается в момент создания объекта.
10) Конструкторы глобальных объектов вызываются до вызова функции main. Локальные объекты создаются, как только становится активной область их действия. Конструктор запускается и при создании временного объекта (например, при передаче объекта из функции).
Слайд 43Примеры конструкторов
class Rectangle
{
private:
float Width;
float Height;
public:
Rectangle() : Width(0), Height(0)
{ /*пустое тело функции*/ }
Rectangle(float w) : Width(w)
{
Height = 0;
}
Rectangle(float w, float h) : Width(w), Height(h)
{ /*пустое тело функции*/ }
}
Слайд 44Вызов конструкторов
(Первая форма)
имя_класса имя_объекта(фактичаские_параметры_конструкора);
Rectangle SS(1.3, 0.22); // SS.Width
= 1.3; SS.Height = 0.22
Rectangle SS(2.3); //
SS.Width = 2.3;
// по умолчанию SS.Height = 0.0
(Вторая форма)
имя_класса(фактические_параметры_конструктора);
Rectangle ZZ = Rectangle(4.0,5,0); // ZZ.Width = 4.0
// ZZ.Height = 5.0
Rectangle ZZ = 4.0;
Rectangle ZZ( 4.0 );
эквивалентна
Если конструктор имеет один параметр, то запись
Слайд 45Копирующий конструктор
class Rectangle
{
private:
float Width;
float Height;
public:
Rectangle() : Width(0), Height(0)
{ /*пустое тело функции*/ }
Rectangle(float w) : Width(w)
{
Height = 0;
}
Rectangle(float w, float h) : Width(w), Height(h)
{ /*пустое тело функции*/ }
}
//----------------------------------------------------------
int main()
{
Rectangle C1(9, 1.2);
Rectangle C2(C1);
Rectangle C3 = C1;
return 0;
}
Слайд 46Конструкторы и параметры по умолчанию
employee::employee(char *name, long employee_id, float salary
= 10000.00)
{
strcpy(employee::name, name);
employee::employee_id = employee_id;
if (salary < 50000.0)
employee::salary = salary;
else //
Недопустимый оклад
employee::salary = 0.0;
};
void main()
{
Employee e1(“Vasia”, 1);
Employee e2(“Petya”, 2, 40000);
}
Слайд 47Деструкторы
Для освобождения этих ресурсов служит особый метод
класса – деструктор.
Имя деструктора совпадает с именем
класса, только перед ним указывается символ ~ (тильда).
Деструктор:
не имеет аргументов и возвращаемого значения;
не может быть объявлен как const или static;
не наследуется;
может быть виртуальным.
Слайд 48 Деструкторы
Данная функция вызывается автоматически при уничтожении экземпляра класса:
для локальных
объектов — при выходе из блока, в котором они объявлены;
для
глобальных — как часть процедуры выхода из main;
для объектов, заданных через указатели, деструктор вызывается неявно при использовании операции delete или delete [].
Слайд 49Пример
class MyFile
{
public:
MyFile():m_pFile(NULL) {}
// конструктор
~MyFile()
// деструктор
{
Close();
}
bool Open(const char *fileName)
{
Close();
m_pFile = fopen(fileName, “r”);
return m_pFile != NULL;
}
void Close()
{
if (m_pFile)
{
fclose(m_pFile);
m_pFile = NULL;
}
}
private:
FILE *m_pFile;
};
Слайд 50Ссылка на себя или указатель this
В метод класса неявно передается
указатель на объект, для которого он вызывается
Rectangle & Max_Area(Rectangle &R)
{
// Вместо this-GetArea(), можно писать GetArea()
if( this->GetArea() > R.GetArea())
{
return *this;
}
else return R;
}
...
Rectangle Rect1(5, 1.7), Rect2(2, 4,8);
// Новый объект Largest инициализируется значениями полей Rect2
Rectangle Largest = Rect1.Max_Area(Rect2);
Слайд 51Статические поля класса
class Rectangle
{
private:
float
Width; int Height;
static int count;
// общее поле для всех объектов
public:
Rectangle (float w, float h) : Width(w), Height(h)
{ count++; }
~Rectangle ()
{ count--; }
int GetCount() // возвращает значение count
{ return count; }
}
int Rectangle::count = 0; // Определение count
//---------------------------------------------------------
int main()
{
Rectangle Rl, R2, R3; // создание трех объектов каждый объект видит
//одно и то же значение
cout << "Число объектов : " << Rl.GetCount() << endl;
cout << "Число объектов : " << R2.GetCount() << endl;
cout << "Число объектов : " << R3.GetCount() << endl;
return 0;
}
Слайд 52 Статические и автоматические поля класса
Объект 1
Объект 3
Объект 2
Автоматические
поля
Автоматические
поля
Автоматические
поля
Статические
поля
Слайд 53 Использование статических полей
1) Память под статическое поле выделяется один
раз при его инициализации независимо от числа созданных объектов (и
даже при их отсутствии).
2) Статические поля доступны как через имя класса, так и через имя объекта:
Например
Rectangle R;
cout << Rectangle::count; // Будет выведено
cout << R.count; // одно и то же
3) На статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя изменить с помощью операции доступа к области действия, как описано выше. Это можно сделать только с помощью статических методов (их мы рассмотрим дальше).
4) Память, занимаемая статическим полем, не учитывается при определении размера объекта с помощью операции sizeof().
Слайд 54Статические методы
class Rectangle
{
private:
. .
.
static int count; // Поле
count - скрытое
public:
. . .
static void inc_count()
{
count++;
}
. . .
};
int Rectangle::count = 1; // Определение в глобальной области
int main()
{
Rectangle R;
// R.count++ - нельзя, т.к. поле count скрытое
// Изменение поля с помощью статического метода
R.inc_count();
cout << "Значение count : " << A::count << endl;
Rectangle::inc_count();
cout << "Значение count : " << A::count << endl;
return 0;
}
Слайд 55Статические методы
Статические методы предназначены для обращения к статическим полям
класса. Они могут обращаться непосредственно только к статическим полям и
вызывать только другие статические методы класса, потому что им не передается скрытый указатель this. Обращение к статическим методам производится так же, как к статическим полям — либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.
Слайд 56class A
{
private:
static int i;
int j;
public:
static int fun()
{
i++;
j++;
}
};
void main()
{
A::fun();
A a = new A();
a.fun();
}
Слайд 57Размещение классов в различных файлах
Общепринятой практикой является размещение объявления классов
в заголовочных файлах *.h, а их реализации – в файлах
*.cpp. Это приводит к то тому что:
Повышается модульность проекта.
Каждый класс может быть подключен для дальнейшего использования при помощи директивы #include “имя заголовочного файла”.
При внесении изменений в реализацию метода класса перекомпиляции подвергнутся только измененные файлы.
Слайд 58Размещение классов в различных файлах
Rectangle.h
class Rectangle
{
private:
float Width;
float Height;
public:
Rectangle() ;
~Rectangle() ;
Rectangle(float w);
float GetArea();
}
Rectangle.cpp
#include “Rectangle.h”
Rectangle::Rectangle ()
{
. . .
}
. . . .
float Rectangle::GetArea()
{
. . .
}
main.cpp
#include “Rectangle.h”
int main()
{
Rectangle R(1,2);
. . .
Return 0;
}