На примере некоторых архитектурных решений Крипты Дмитрий расскажет о способах реализации полиморфного поведения в программах на C++, о преимуществах и недостатках этих способов, а также о новых возможностях C++11.
2. 2
Статический и
динамический
полиморфизм в C++
Дмитрий Леванов
Ведущий разработчик Крипта
3. 3
Крипта
Отвечает на вопрос «Кто?»
Определяет интересы по поведению в
интернете
Используется для таргетинга рекламы
От др.-греч. κρυπτή — крытый подземный ход,
тайник
4. 4
Как учили Крипту
+ Логи
Обучение Контроль
Матрикснет
6. 6
Разработка Крипты
Много логов в разных форматах
Сложные цепочки обработки
Высокие требования к производительности
Много одинаковой похожей логики
Хочется делать всё однообразно
8. 8
Полиморфизм
Способ поставить в соответствие некой
грамматической конструкции контекстно-
зависимую семантику
или, по-русски:
Текст программы [почти] один и тот же, а
смысл разный
9. 9
Виртуальный полиморфизм
struct Base {
virtual void do() { std::cout << “base”; }
};
struct Derived : public Base {
virtual void do() { std::cout << “derived”; }
};
Base* b = new Derived();
b->do(); // derived
ООП-шненько
Типобезопасно
Работают фичи, зависящие от _vptr
10. Виртуальный полиморфизм: минусы
Медленный вызов методов
Надо поддерживать иерархию классов
Приходится иметь дело с T* или T&
Грабли с виртуальными методами
Статический метод не может быть виртуальным
Инвариантность параметров
10
11. 11
Зачастую все сводится к…
void for_each(const vector<int>& v, const Handler& h) {
for (int i : v) {
h.handle(i);
}
}
//...
vector<int> vect = {1,2,3};
MyHandler handler;
for_each(vect, handler);
12. 12
То же самое, но лучше
template<typename Handler>
void for_each(const vector<int>& v, const Handler& h) {
for (int i : v) {
h.handle(i);
}
}
//...
vector<int> vect = {1,2,3};
MyHandler handler;
for_each(vect, handler);
14. 14
Совсем хорошо
template<typename Handler>
void for_each(const vector<int>& v, const Handler& h) {
for (int i : v) {
h(i);
}
}
//...
vector<int> vect = {1,2,3};
for_each(vect, [](int i){ cout << i; });
15. 15
Или так
template<typename Handler>
void for_each(const vector<int>& v) {
for (int i : v) {
Handler::handle(i);
}
}
//...
vector<int> vect = {1,2,3};
MyHandler handler;
for_each<MyHandler>(vect);
16. 16
Статический полиморфизм: плюсы
Типобезопасно
Быстрый вызов методов
Не надо наследоваться
Не надо иметь дело с указателями
Контрвариантность параметров
Можно использовать лямбды
17. Статический полиморфизм: минусы
17
Нельзя положить в коллекцию
Сложно проверять правильность кода
Медленно компилируется
Может распухнуть бинарник
Нет поддержки концептов
Есть ограничения компилятора
Не во всех IDE правильно работает
автокомплит
23. «Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
23
template<typename Derived>
class Base {
void do() {
Derived::do();
}
};
24. «Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
24
template<typename Derived>
class Base {
void do() {
Derived::do();
}
};
class MyDerived : public Base<MyDerived> {
void do() { std::cout << "my derived"; }
};
25. «Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
25
template<typename Derived>
class Base {
void do() {
Derived::do();
}
};
class MyDerived : public Base<MyDerived> {
void do() { std::cout << "my derived"; }
};
Base<MyDerived>* b = new MyDerived();
b->do(); // my derived
26. «Виртуальный» вызов без virtual
a.k.a. Curiously Recurring Template Pattern
26
template<typename Derived>
class Base {
void do() {
static_cast<Derived*>(this)->do();
}
};
class MyDerived : public Base<MyDerived> {
void do() { std::cout << "my derived"; }
};
Base<MyDerived>* b = new MyDerived();
b->do(); // my derived
27. 27
CRTP
Идеально подходит для шаблона
проектирования «Template method»
«Виртуальный» метод может быть
статическим
Работает в ~7 раз быстрее виртуальной
версии
29. 29
Tag dispatching
template <class InputIter, class Dist>
void advance (InputIter& it, Dist n);
template <class InputIter, class Dist>
void advance(InputIter& i, Dist n) {
while (n--) ++i;
}
template <class RndAcsIter, class Dist>
void advance(RndAcsIter& i, Dist n) {
i += n;
}
30. 30
Tag dispatching
template <class InputIter, class Dist>
void advance (InputIter& it, Dist n);
template <class InputIter, class Dist>
void advance(InputIter& i, Dist n, input_iter_tag) {
while (n--) ++i;
}
template <class RndAcsIter, class Dist>
void advance(RndAcsIter& i, Dist n, rnd_acs_iter_tag) {
i += n;
}
31. 31
Tag dispatching
template <class InputIter, class Dist>
void advance (InputIter& it, Dist n) {
typename iter_traits<InputIter>::iter_category cat;
advance(i, n, cat);
}
template <class InputIter, class Dist>
void advance(InputIter& i, Dist n, input_iter_tag) {
while (n--) ++i;
}
template <class RndAcsIter, class Dist>
void advance(RndAcsIter& i, Dist n, rnd_acs_iter_tag) {
i += n;
}
33. 33
Задача
Например, мы пишем дебаггер
Есть множество объектов, не связанных
какой-либо иерархией
Хотим сложить их в одну коллекцию,
проитерироваться по ней, и сдампить объекты
int x = 10;
Foo bar;
objects.add(x);
objects.add(bar);
for (const auto& obj : objects) {
obj.dump();
}
37. 37
External polymorphism
class Dumper {
std::vector<Dumpable*> dumpables;
public:
template<typename T>
void add(T& obj) {
auto dumpable = new ConcreteDumpable<T>(obj);
dumpables.push_back(dumpable);
}
void dumpAll() const {
for (auto d : dumpables) { d->dump(); }
}
};
38. 38
External polymorphism
class Dumper {
std::vector<Dumpable*> dumpables;
public:
template<typename T>
void add(T& obj) {
auto dumpable = new ConcreteDumpable<T>(obj);
dumpables.push_back(dumpable);
}
void dumpAll() const {
for (auto d : dumpables) { d->dump(); }
}
} dumper;
int x = 10;
Foo bar;
dumper.add(x);
dumper.add(foo);
dumper.dumpAll();
39. 39
External polymorphism
Симбиоз виртуального и статического
полиморфизма
Для поддержки нового типа T надо добавить
только ::dump(T)
Можно строить параллельные иерархии
48. 48
Новые возможности C++11:
std::function
Медленные (~10 раз медленнее шаблонной
функции)
Обеспечивают поддержку концептов
Позволяют сохранить исполняемые объекты
49. 49
Новые возможности C++11:
std::function
Медленные (~10 раз медленнее шаблонной
функции)
Обеспечивают поддержку концептов
Позволяют сохранить исполняемые объекты
Не замена шаблонам и виртуальным методам!