2. Содержание
Указатель на функцию
Наследование
Виртуальные функции
Полиморфизм на основе виртуальных функций
Абстрактные классы
Перегрузка операций
Функциональные объекты
Шаблоны
Статический полиморфизм
Специализация шаблонов
Некоторые особенности стандарта C++11
3. Указатель на функцию
Указателем на функцию называется адрес точки
входа функции.
Объявление переменной - указателя на
функцию:
void (*pfunc)(double*, const size_t);
Объявление типа указателя на функцию:
typedef void (*pfunc_t)(double*, const size_t);
5. Наследование
Наследование – механизм, позволяющий
описать новый класс (потомок,
наследник, производный класс) на основе
существующего (предок, базовый класс,
родительский класс), с сохранением
функциональности родительского класса.
Наследование позволяет обращаться с
производными классами так же, как и с их
родительскими классами.
Указатель на объект базового класса
может ссылаться и на объект
производного класса. Обратное
утверждение неверно.
Пример наследования:
class Base {
int value;
public:
Base(): value(0){}
int getValue() { return value; }
};
class Derived: public Base{
public:
Derived() : Base() {}
void setValue(int data) {
value = data;
}
};
6. Наследование:
пример использования
class Entity{
protected:
std::string name;
Entity* parent;
public:
virtual std::string ToString() const = 0;
Entity* GetParent() const{ return parent; }
};
class Folder: public Entity{
std::list<Entity*> childs;
public:
Folder(std::string n, Entity* p)
: name(n), parent(p){}
std::string ToString() const { return name; }
};
7. Виртуальные функции
Виртуальной функцией класса
называют метод, который может быть
переопределён в потомке таким
образом, что конкретная реализация
функции для вызова будет
определяться во время исполнения.
Виртуальные функции являются одной
из основ реализации динамического
полиморфизма.
Пример:
class Base{
public:
Base(): value(0){}
virtual void show() {
std::cout << "Base" << std::endl;
}
};
class Derived: public Base{
public:
Derived() : Base() {}
virtual void show() {
std::cout << «Derived" << std::endl;
}
};
8. Виртуальные функции:
пример полиморфизма
class Base{
public:
Base(): value(0){}
virtual void show() { std::cout << "Base" << std::endl; }
};
class Derived: public Base{
public:
Derived() : Base() {}
virtual void show() { std::cout << "Derived" << std::endl; }
};
void print(Base* p) { p->show(); }
int main(){
Base* b = new Base();
Derived* d = new Derived();
print(b);
print(d);
return 0;
}
9. Виртуальные функции:
абстрактные классы
Если базовый класс только декларирует
существование виртуального метода, но
не предполагает его реализации, то такой
метод называется чистым виртуальным
или абстрактным.
Класс, содержащий хотя бы один
абстрактный метод, называется
абстрактным классом.
Любая абстрактная функция должна быть
определена в дочернем классе.
Абстрактные классы используются в том
случае, когда программист хочет
определить семейство классов с
одинаковым поведением.
struct Shape{
virtual void Draw() = 0;
virtual void Hide() = 0;
};
struct Point: Shape{
virtual void Draw(){
// draw this point
}
virtual void Hide(){
// hide this point
}
};
Абстрактные классы очень похожи
на интерфейсы из Java и C#
10. Перегрузка операций
полезно переопределить некоторый
оператор для класса, тогда запись
программы становится более
компактной и понятной.
Общая форма определения оператора
выглядит следующим образом:
<return_type> operator <operator_sig
n> (<operator_parametres>);
для определения, где лучше всего
определить оператор - внутри класса
или вне его, - спросите себя, изменяет
ли этот оператор свои входные
параметры. Если изменяет - то этот
оператор - часть класса. Если не
изменяет, то оператор лучше всего
определить вне класса.
сlass int_pair{
int first, second;
public:
int_pair(int f = 0, int s = 0): first(f),
second(s) {}
bool operator == (const int_pair&
p) const {
return (first == p.first) && (second
== p.second);
}
};
bool operator != (const int_pair&
p1, const int_pair& p2){
return !(p1 == p2);
}
11. Функциональные объекты
Если в классе переопределен оператор (),
то объекты этого класса получают
свойства функций (их можно
использовать как функции). Такие
объекты называются функциональными
или функторами.
Функторы удобно использовать, когда
функция должна обладать "памятью", а
также, как замена указателей на функции.
// функтор, подсчитывающих количество
вызовов
class _swap{
static size_t counter = 0;
static void increment() { ++counter; }
public:
_swap(){}
void operator ()(int& a, int& b){
int tmp = a;
a = b;
b = tmp;
inc();
}
int getNrCalls() {return count; }
};
_swap swap;
int a = 3, b = 5;
swap(a, b);
12. шаблоны
Шаблонами классов
называется семейство
классов с одинаковым
поведением
Шаблонами функций
называют семейство
функций с одинаковым
поведением
template<class TYPE>
void swap(TYPE& p1, TYPE& p2){
TYPE tmp = p1;
p1 = p2;
p2 = tmp;
}
Понятие шаблона функции часто
ассоциируется с понятием алгоритма
13. Шаблоны:
статический полиморфизм
int main(){
int i1 = 3, i2 = 5;
float f1 = 1.2, f2 = 2.3;
double d1 = 1.003, d2
= 10;
swap(i1, i2);
swap(f1, f2);
swap(d1, d2);
return 0;
}
Одно и то же имя
функции используется с
различными типами
данных. Данное свойство
называется статическим
полиморфизмом, потому
что определение
поведения функции
выполняется на этапе
компиляции программы.
14. Шаблоны:
специализация шаблонов
Для какого-то типа данных
можно указать
специальную версию
шаблона.
template<>
void swap(int* a, int* b){
int tmp = *a;
*a = *b;
*b = tmp;
}
Если шаблон (его специализация) не
используются в программе, то на этапе
компиляции он(а) не включается в
бинарный код!
18. C++11: нулевой указатель
Использование в качестве
нулевого указателя значения 0
в некоторых случаях
приводило к опасному коду.
Рекомендуется использовать
ключевое слово nullptr
void test(int);
void test(char*);
int main(){
test(0); // ???
test(nullptr); // !!!
return true;
}
19. С++11: цикл «для каждого»
int a[5] = {1, 2, 3, 4, 5};
for(auto& el: a){
++el;
}
for(auto el: a){
std::cout << el << " ";
}
В новом стандарте С++
предлагается новая
форма записи цикла
for: for( type element:
collection){}