ݺߣ

ݺߣShare a Scribd company logo
Шаблоны
ЛЕКЦИЯ	
  №7
Два	
  вида	
  многократного	
  
использования	
  кода
Наследование
• Создаем	
  структуру	
  для	
  работы	
  с	
  
«базовым	
  классом»
• Создаем	
  классы-­‐наследники	
  на	
  каждый	
  
случай.
Шаблоны
• Описываем	
  «стратегию	
  работы»	
  с	
  
«неопределенным»	
  классом.
• Компилятор	
  в	
  момент	
  создание	
  класса	
  по	
  
шаблону,	
  сам	
  создает	
  нужный	
  «код»	
  для	
  
конкретного	
  класса.
Template	
  это	
  …
• Шаблон	
  – это	
  параметрическая	
  функция	
  или	
  класс.
• Параметром	
  может	
  являться	
  как	
  значение	
  переменной	
  (как	
  в	
  
обычных	
  функциях)	
  так	
  и	
  тип	
  данных.
• Параметры	
  подставляются	
  на	
  этапе	
  компиляции	
  программы.
• Подставляя	
  параметры	
  в	
  шаблон	
  – мы	
  конструируем	
  новый	
  тип	
  
данных	
  (или	
  функцию,	
  если	
  это	
  шаблон	
  функции)	
  
Простой	
  шаблон
Example50_Template
template <class T> class Print
{
public:
Print(T value)
{
std::cout << "Value:" << value <<
std::endl;
};
};
Перед	
  описанием	
  класса	
  ставим	
  ключевое	
  
слово	
  template	
  <class	
  T>
T	
  – используем	
  вместо	
  имени	
  класса,	
  
который	
  будет	
  заменяться	
  при	
  создании	
  
конкретного	
  экземпляра	
  класса.
Print – это	
  шаблон
Print<int> -­‐ это	
  класс,	
  
сконструированный	
  по	
  шаблону
Несколько	
  параметров	
  и	
  шаблоны-­‐функции
Example51_MultiTemplate
Параметры	
  указываются	
  через	
  запятую:
template <class A, class B> class Sum { …}
Оператор,	
  принимающий	
  в	
  качестве	
  параметра	
  – шаблон	
  с	
  
параметрами:
template <class A, class B> std::ostream& operator<<(std::ostream & os, Sum<A, B> &sum)
Параметры	
  – переменные
Example52_ComplexParameters
1.template <class TYPE, TYPE def_value, size_t SIZE = 10 > class Array {
2.protected:
3. TYPE _array[SIZE];
4.public:
5. Array() {
6. for (int i = 0; i < SIZE; i++) {
7. _array[i] = def_value;
8. }
9. }
10. const size_t size() {
11. return SIZE;
12. }
13. const TYPE operator[](size_t index) {
14. if ((index >= 0) && (index < SIZE)) return _array[index];
15. else throw BadIndexException(index, SIZE);
16. }
17.};
Специализация	
  шаблонов
Example53_TemplateSpecialization
template <class T>
class mycontainer {
// …
};
template <>
class mycontainer <char> {
// ..
};
Иногда	
  бывает	
  необходимость	
  сделать	
  
специальную	
  реализацию	
  шаблона	
  для	
  
какого-­‐либо	
  типа.
В	
  этом	
  случае,	
  можно	
  описать	
  отдельную	
  
реализацию	
  класса,	
  дополнив	
  его	
  новыми	
  
методами	
  или	
  переопределив	
  реализацию	
  
существующих.
Можно	
  специализировать	
  только	
  часть	
  
параметров
Example54_TemplateSpecialization2
template <class A, class B,
class C> class Sum {
…
}
template <class A, class B>
class Sum<A, B, const char*> {
…
}
При	
  частичной	
  специализации	
  у	
  шаблона	
  
становится	
  меньше	
  параметров	
  (какие-­‐то	
  
мы	
  уже	
  указали	
  явно).
Частичная	
  специализация	
  работает	
  только	
  с	
  
классами	
  (с	
  функциями	
  не	
  работает).
Вычисляем	
  факториал
Example54_Factorial
1.// факториал с помощью функций
2.template <uint64_t value> uint64_t Factorial(){
3. return Factorial<value-1>()*value;
4.}
5.template <> uint64_t Factorial<0>(){
6. return 1;
7.}
8.// факториал с помощью классов
9.template<uint64_t n>class fact{
10. public:
11. static const uint64_t value = fact<n-1>::value * n;
12.};
13. template<>class fact<0>{
14. public:
15. static const uint64_t value = 1;
16.};
Templates
две	
  модели
1.	
  Наиболее	
  популярный	
  подход	
  -­‐ модель	
  
включения(inclusion model),	
  определения	
  
шаблонов	
  полностью	
  размещаются	
  в	
  
заголовочном	
  файле.
2.	
  Модель	
  явного	
  инстанцирования (explicit
instantiation model),	
  как	
  правило	
  реализуется	
  
директивой	
  явного	
  инстанцирования (explicit
instantiation directive).
Inclusion	
  model
template<class T> class stack {
T* v;
T* p;
int sz;
public:
stack(int s) { v = p = new T[sz=s]; }
~stack() { delete[] v; }
void push(T a) { *p++ = a; }
T pop() { return *--p; }
int size() const { return p-v; }
};
И	
  объявление	
  и	
  описание	
  шаблона	
  
располагается	
  в	
  header	
  файле	
  (.h)
Фактически,	
  при	
  любом	
  
подключении	
  к	
  .cpp файлу	
  – это	
  
будет	
  новый	
  шаблон	
  для	
  
компилятора.
Минус	
  такой	
  модели	
  в	
  том,	
  что	
  
трудно	
  читать	
  код	
  (все	
  перемешано).
explicit instantiation model
Example55_ExplicitInstantiation
template <class T> class
MyStack
{
public:
MyStack(void);
};
template <class T>
MyStack<T>::MyStack(void)
{
_size = 0;
_current = NULL;
}
template class MyStack<class
MyClass>;
В	
  продолжение	
  примера
1. В	
  качестве	
  параметра	
  шаблона	
  можно	
  передавать	
  указатели	
  на	
  функции	
  (если	
  работать	
  
не	
  с	
  указателями	
  – то	
  это	
  уже	
  будет	
  вызов	
  функции	
  J )
2. В	
  примере	
  «параметр-­‐функция»	
  нам	
  понадобился	
  что	
  бы	
  удалять	
  указатели.	
  Если	
  бы	
  
мы	
  в	
  коде	
  написали	
  «delete	
  old-­‐>item»	
  то	
  такой	
  код	
  не	
  скомпилировался	
  бы	
  для	
  класса	
  
MyStack<MyClass>.
3. А	
  вот	
  для	
  MyStack<MyClass*>	
  -­‐ скомпилировался	
  бы.
Шаблоны	
  с	
  переменным	
  числом	
  
параметров
Example56_VariadicTemplate
template <class T> void print(const T&
t) {
std::cout << t << std::endl;
}
template <class First, class... Rest>
void print(const First& first, const
Rest&... rest) {
print(rest...);
}
• В	
  C++	
  есть	
  возможность	
  сделать	
  шаблон	
  с	
  
переменным	
  числом	
  параметров.
• В	
  этом	
  случае	
  используется	
  «…»	
  для	
  
указания	
  списка	
  параметров.
• Работать	
  с	
  такими	
  шаблонами	
  можно	
  по	
  
принципу	
  рекурсии.
Variadic template	
  в	
  структурах	
  данных
Example57_VariadicTemplate2
1.// Конец рекурсии
2.template <class... Ts> class tuple {};
3.// Шаблон
4.template <class T, class... Ts>
5.// Класс наследник самого себя но с меньшим числом параметров
6.class tuple<T, Ts...> : public tuple<Ts...> {
7. public:
8. tuple(T t, Ts... ts) : tuple<Ts...>(ts...), value(t) {}
9.// Ссылка на родителя
10. tuple<Ts...> &next = static_cast<tuple<Ts...>&>(*this);
11.// Последний параметр
12. T value;
13.};
Что	
  внутри?
1.class tuple<double, uint64_t, const char*> : public tuple<uint64_t, const char*>
{
2. double value;
3.}
4.class tuple<uint64_t, const char*> : public tuple<const char*> {
5. uint64_t value;
6.}
7.class tuple<const char*> : public tuple {
8. const char* value;
9.}
10.class tuple {
11.}
std::enable_if
1. // Шаблон
2. template<bool, typename _Tp = void>
3. struct enable_if { };
4. // Специализация шаблона, в случае если параметр - истина
5. template<typename _Tp>
6. struct enable_if<true, _Tp>{
7. typedef _Tp type;
8.};
Вспомогательный	
  тип
1.// специальная структура для определения типа конкретного элемента в
tuple
2.template <size_t, class> struct elem_type_holder;
3.// без параметра - это тип базового класса
4.template <class T, class... Ts> struct elem_type_holder<0, tuple<T,
Ts...>> {
5. typedef T type; // тип
6.};
7.// это тип k-го класса в цепочке наследования
8.template <size_t k, class T, class... Ts> struct elem_type_holder<k,
tuple<T, Ts...>> {
9.typedef typename elem_type_holder<k - 1, tuple<Ts...>>::type type;
10.};
Шаблонная	
  функция	
  get
Example57_VariadicTemplate2Full
1.// шаблон функции get для получения параметра (данная специализация работает только при
index==0)
2.template <size_t index,class ...Ts>
3.typename std::enable_if<index == 0,
4. typename elem_type_holder<0, tuple<Ts...>>::type&>::type
5.get(tuple<Ts...>& t){
6. return t.value;
7.}
8.// шаблон функции get для получения параметра (данная специализация работает только при
index!=0)
9.template <size_t index,class T,class ...Ts>
10.typename std::enable_if<index != 0,
11. typename elem_type_holder<index, tuple<T,Ts...>>::type&>::type
12.get(tuple<T,Ts...>& t){
13. tuple<Ts...> &base = t.next;
14. return get<index-1>(base);
15.}
CRTP	
  (Curiously	
  Recurring	
  Template	
  Pattern)
Example60_CRTP
template <class T>
class base{};
class derived : public
base<derived> {};
Такая	
  конструкция	
  делает	
  возможным	
  
обращение	
  к	
  производному	
  классу	
  из	
  
базового!
Множественное	
  наследование	
  в	
  шаблонах
Example58_VariadicTemplate3
1.template <typename... BaseClasses>
2.class Printer : public BaseClasses... {
3.public:
4.Printer(BaseClasses&&... base_classes) :
BaseClasses(base_classes)...
5.{
6.}
7.};
Шаблоны	
  в	
  качестве	
  параметров	
  шаблонов
Example59_TemplateParameter
• Шаблон	
  можно	
  указать	
  в	
  
качестве	
  параметра	
  шаблона!
• Все	
  типы,	
  которые	
  
используются	
  при	
  
конструировании	
  нового	
  типа	
  с	
  
помощью	
  шаблона	
  – должны	
  
быть	
  его	
  параметрами.
template <class T> class Payload
{
…
};
template <template <class> class
PL, class T> class Printer
{
…
};
Printer<Payload, int> printer;
Что	
  нового	
  в	
  C++:	
  auto
Example62_Auto
В	
  С++11 auto позволяет	
  не	
  указывать	
  тип	
  переменной	
  явно,	
  говоря	
  компилятору,	
  чтобы	
  он	
  
сам	
  определил	
  фактический	
  тип	
  переменной,	
  на	
  основе	
  типа	
  инициализируемого	
  
значения.	
  Это	
  может	
  использоваться	
  при	
  объявлении	
  переменных	
  в	
  различных	
  областях	
  
видимости,	
  как,	
  например,	
  пространство	
  имен,	
  блоки,	
  инициализация	
  в	
  цикле	
  и	
  т.п.
auto	
  i =	
  42;	
  	
  	
  	
  	
  	
  	
  	
  //	
  i -­‐ int
auto	
  l	
  =	
  42LL;	
  	
  	
  	
  	
  	
  //	
  l	
  -­‐ long	
  long
auto	
  p	
  =	
  new	
  foo();	
  //	
  p	
  -­‐ foo*
Использование auto позволяет	
  сократить	
  код	
  (если,	
  конечно,	
  тип	
  не int,	
  который	
  на	
  одну	
  
букву	
  меньше).
Не	
  может	
  использоваться	
  в	
  объявлении	
  параметров	
  функции	
  или	
  класса;
Протечка	
  абстракции
Неудобной	
  составляющей	
  работы	
  с	
  коллекциями	
  
объектов	
  родительского	
  типа	
  является	
  необходимость	
  
приведения	
  родительского	
  типа	
  к	
  типу-­‐наследнику	
  (для	
  
выполнения	
  необходимых	
  операций	
  над	
  элементом	
  
коллекции).	
  Т.е.	
  мы	
  жертвуем	
  статическим	
  контролем	
  
типов.
Протеска	
  абстракции	
  номер	
  1
Example66_AbstractionLeak1
1. class Figure{
2. public:
3. virtual double Square()=0;};
4. class Circle : public Figure{
5. public:
6. double Square() override{
7. return 3.14*3.14*R;};};
8. class Sphere : public Circle{
9. public:
10. double Volume() {
11. return 3.14*3.14*3.14*R; };
12. };
// abstraction leak
for(Figure *figure:array)
{
Sphere *sphere = dynamic_cast<Sphere*> (figure);
if(sphere!=nullptr)
std::cout << "Volume:"
<< sphere->Volume()
<< std::endl;
}
Протечка	
  абстракции	
  2
Example66_AbstractionLeak2
1.class Figure{
2.public:
3. virtual double Square()=0;
4. virtual double Volume() { return 0.0;};
5. virtual ~Figure() {};
6.};
7. Figure* array[]={new Circle(1),new Circle (2),new Sphere(1)};
8. // ISP (Interface Segregation Principle) fail
9. for(Figure *figure:array) std::cout << "Square:" << figure->Square() << std::endl;
10. for(Figure *figure:array) std::cout << "Volume:" << figure->Volume() << std::endl;
Используем	
  tuple
Example68_AbstractionLeak3
1. tuple<Circle,Circle,Sphere,Sphere>
2. t(Circle(1),Circle(2),Sphere(1),Sphere(2));
3.// в параметры get<size_t> можно подставить только константу
4. std::cout << "Square:" << get<0>(t).Square() << std::endl;
5. std::cout << "Volume:" << get<3>(t).Volume() << std::endl;

More Related Content

Объектно-ориентированное программирование. Лекция 7 и 8.

  • 2. Два  вида  многократного   использования  кода Наследование • Создаем  структуру  для  работы  с   «базовым  классом» • Создаем  классы-­‐наследники  на  каждый   случай. Шаблоны • Описываем  «стратегию  работы»  с   «неопределенным»  классом. • Компилятор  в  момент  создание  класса  по   шаблону,  сам  создает  нужный  «код»  для   конкретного  класса.
  • 3. Template  это  … • Шаблон  – это  параметрическая  функция  или  класс. • Параметром  может  являться  как  значение  переменной  (как  в   обычных  функциях)  так  и  тип  данных. • Параметры  подставляются  на  этапе  компиляции  программы. • Подставляя  параметры  в  шаблон  – мы  конструируем  новый  тип   данных  (или  функцию,  если  это  шаблон  функции)  
  • 4. Простой  шаблон Example50_Template template <class T> class Print { public: Print(T value) { std::cout << "Value:" << value << std::endl; }; }; Перед  описанием  класса  ставим  ключевое   слово  template  <class  T> T  – используем  вместо  имени  класса,   который  будет  заменяться  при  создании   конкретного  экземпляра  класса. Print – это  шаблон Print<int> -­‐ это  класс,   сконструированный  по  шаблону
  • 5. Несколько  параметров  и  шаблоны-­‐функции Example51_MultiTemplate Параметры  указываются  через  запятую: template <class A, class B> class Sum { …} Оператор,  принимающий  в  качестве  параметра  – шаблон  с   параметрами: template <class A, class B> std::ostream& operator<<(std::ostream & os, Sum<A, B> &sum)
  • 6. Параметры  – переменные Example52_ComplexParameters 1.template <class TYPE, TYPE def_value, size_t SIZE = 10 > class Array { 2.protected: 3. TYPE _array[SIZE]; 4.public: 5. Array() { 6. for (int i = 0; i < SIZE; i++) { 7. _array[i] = def_value; 8. } 9. } 10. const size_t size() { 11. return SIZE; 12. } 13. const TYPE operator[](size_t index) { 14. if ((index >= 0) && (index < SIZE)) return _array[index]; 15. else throw BadIndexException(index, SIZE); 16. } 17.};
  • 7. Специализация  шаблонов Example53_TemplateSpecialization template <class T> class mycontainer { // … }; template <> class mycontainer <char> { // .. }; Иногда  бывает  необходимость  сделать   специальную  реализацию  шаблона  для   какого-­‐либо  типа. В  этом  случае,  можно  описать  отдельную   реализацию  класса,  дополнив  его  новыми   методами  или  переопределив  реализацию   существующих.
  • 8. Можно  специализировать  только  часть   параметров Example54_TemplateSpecialization2 template <class A, class B, class C> class Sum { … } template <class A, class B> class Sum<A, B, const char*> { … } При  частичной  специализации  у  шаблона   становится  меньше  параметров  (какие-­‐то   мы  уже  указали  явно). Частичная  специализация  работает  только  с   классами  (с  функциями  не  работает).
  • 9. Вычисляем  факториал Example54_Factorial 1.// факториал с помощью функций 2.template <uint64_t value> uint64_t Factorial(){ 3. return Factorial<value-1>()*value; 4.} 5.template <> uint64_t Factorial<0>(){ 6. return 1; 7.} 8.// факториал с помощью классов 9.template<uint64_t n>class fact{ 10. public: 11. static const uint64_t value = fact<n-1>::value * n; 12.}; 13. template<>class fact<0>{ 14. public: 15. static const uint64_t value = 1; 16.};
  • 10. Templates две  модели 1.  Наиболее  популярный  подход  -­‐ модель   включения(inclusion model),  определения   шаблонов  полностью  размещаются  в   заголовочном  файле. 2.  Модель  явного  инстанцирования (explicit instantiation model),  как  правило  реализуется   директивой  явного  инстанцирования (explicit instantiation directive).
  • 11. Inclusion  model template<class T> class stack { T* v; T* p; int sz; public: stack(int s) { v = p = new T[sz=s]; } ~stack() { delete[] v; } void push(T a) { *p++ = a; } T pop() { return *--p; } int size() const { return p-v; } }; И  объявление  и  описание  шаблона   располагается  в  header  файле  (.h) Фактически,  при  любом   подключении  к  .cpp файлу  – это   будет  новый  шаблон  для   компилятора. Минус  такой  модели  в  том,  что   трудно  читать  код  (все  перемешано).
  • 12. explicit instantiation model Example55_ExplicitInstantiation template <class T> class MyStack { public: MyStack(void); }; template <class T> MyStack<T>::MyStack(void) { _size = 0; _current = NULL; } template class MyStack<class MyClass>;
  • 13. В  продолжение  примера 1. В  качестве  параметра  шаблона  можно  передавать  указатели  на  функции  (если  работать   не  с  указателями  – то  это  уже  будет  вызов  функции  J ) 2. В  примере  «параметр-­‐функция»  нам  понадобился  что  бы  удалять  указатели.  Если  бы   мы  в  коде  написали  «delete  old-­‐>item»  то  такой  код  не  скомпилировался  бы  для  класса   MyStack<MyClass>. 3. А  вот  для  MyStack<MyClass*>  -­‐ скомпилировался  бы.
  • 14. Шаблоны  с  переменным  числом   параметров Example56_VariadicTemplate template <class T> void print(const T& t) { std::cout << t << std::endl; } template <class First, class... Rest> void print(const First& first, const Rest&... rest) { print(rest...); } • В  C++  есть  возможность  сделать  шаблон  с   переменным  числом  параметров. • В  этом  случае  используется  «…»  для   указания  списка  параметров. • Работать  с  такими  шаблонами  можно  по   принципу  рекурсии.
  • 15. Variadic template  в  структурах  данных Example57_VariadicTemplate2 1.// Конец рекурсии 2.template <class... Ts> class tuple {}; 3.// Шаблон 4.template <class T, class... Ts> 5.// Класс наследник самого себя но с меньшим числом параметров 6.class tuple<T, Ts...> : public tuple<Ts...> { 7. public: 8. tuple(T t, Ts... ts) : tuple<Ts...>(ts...), value(t) {} 9.// Ссылка на родителя 10. tuple<Ts...> &next = static_cast<tuple<Ts...>&>(*this); 11.// Последний параметр 12. T value; 13.};
  • 16. Что  внутри? 1.class tuple<double, uint64_t, const char*> : public tuple<uint64_t, const char*> { 2. double value; 3.} 4.class tuple<uint64_t, const char*> : public tuple<const char*> { 5. uint64_t value; 6.} 7.class tuple<const char*> : public tuple { 8. const char* value; 9.} 10.class tuple { 11.}
  • 17. std::enable_if 1. // Шаблон 2. template<bool, typename _Tp = void> 3. struct enable_if { }; 4. // Специализация шаблона, в случае если параметр - истина 5. template<typename _Tp> 6. struct enable_if<true, _Tp>{ 7. typedef _Tp type; 8.};
  • 18. Вспомогательный  тип 1.// специальная структура для определения типа конкретного элемента в tuple 2.template <size_t, class> struct elem_type_holder; 3.// без параметра - это тип базового класса 4.template <class T, class... Ts> struct elem_type_holder<0, tuple<T, Ts...>> { 5. typedef T type; // тип 6.}; 7.// это тип k-го класса в цепочке наследования 8.template <size_t k, class T, class... Ts> struct elem_type_holder<k, tuple<T, Ts...>> { 9.typedef typename elem_type_holder<k - 1, tuple<Ts...>>::type type; 10.};
  • 19. Шаблонная  функция  get Example57_VariadicTemplate2Full 1.// шаблон функции get для получения параметра (данная специализация работает только при index==0) 2.template <size_t index,class ...Ts> 3.typename std::enable_if<index == 0, 4. typename elem_type_holder<0, tuple<Ts...>>::type&>::type 5.get(tuple<Ts...>& t){ 6. return t.value; 7.} 8.// шаблон функции get для получения параметра (данная специализация работает только при index!=0) 9.template <size_t index,class T,class ...Ts> 10.typename std::enable_if<index != 0, 11. typename elem_type_holder<index, tuple<T,Ts...>>::type&>::type 12.get(tuple<T,Ts...>& t){ 13. tuple<Ts...> &base = t.next; 14. return get<index-1>(base); 15.}
  • 20. CRTP  (Curiously  Recurring  Template  Pattern) Example60_CRTP template <class T> class base{}; class derived : public base<derived> {}; Такая  конструкция  делает  возможным   обращение  к  производному  классу  из   базового!
  • 21. Множественное  наследование  в  шаблонах Example58_VariadicTemplate3 1.template <typename... BaseClasses> 2.class Printer : public BaseClasses... { 3.public: 4.Printer(BaseClasses&&... base_classes) : BaseClasses(base_classes)... 5.{ 6.} 7.};
  • 22. Шаблоны  в  качестве  параметров  шаблонов Example59_TemplateParameter • Шаблон  можно  указать  в   качестве  параметра  шаблона! • Все  типы,  которые   используются  при   конструировании  нового  типа  с   помощью  шаблона  – должны   быть  его  параметрами. template <class T> class Payload { … }; template <template <class> class PL, class T> class Printer { … }; Printer<Payload, int> printer;
  • 23. Что  нового  в  C++:  auto Example62_Auto В  С++11 auto позволяет  не  указывать  тип  переменной  явно,  говоря  компилятору,  чтобы  он   сам  определил  фактический  тип  переменной,  на  основе  типа  инициализируемого   значения.  Это  может  использоваться  при  объявлении  переменных  в  различных  областях   видимости,  как,  например,  пространство  имен,  блоки,  инициализация  в  цикле  и  т.п. auto  i =  42;                //  i -­‐ int auto  l  =  42LL;            //  l  -­‐ long  long auto  p  =  new  foo();  //  p  -­‐ foo* Использование auto позволяет  сократить  код  (если,  конечно,  тип  не int,  который  на  одну   букву  меньше). Не  может  использоваться  в  объявлении  параметров  функции  или  класса;
  • 24. Протечка  абстракции Неудобной  составляющей  работы  с  коллекциями   объектов  родительского  типа  является  необходимость   приведения  родительского  типа  к  типу-­‐наследнику  (для   выполнения  необходимых  операций  над  элементом   коллекции).  Т.е.  мы  жертвуем  статическим  контролем   типов.
  • 25. Протеска  абстракции  номер  1 Example66_AbstractionLeak1 1. class Figure{ 2. public: 3. virtual double Square()=0;}; 4. class Circle : public Figure{ 5. public: 6. double Square() override{ 7. return 3.14*3.14*R;};}; 8. class Sphere : public Circle{ 9. public: 10. double Volume() { 11. return 3.14*3.14*3.14*R; }; 12. }; // abstraction leak for(Figure *figure:array) { Sphere *sphere = dynamic_cast<Sphere*> (figure); if(sphere!=nullptr) std::cout << "Volume:" << sphere->Volume() << std::endl; }
  • 26. Протечка  абстракции  2 Example66_AbstractionLeak2 1.class Figure{ 2.public: 3. virtual double Square()=0; 4. virtual double Volume() { return 0.0;}; 5. virtual ~Figure() {}; 6.}; 7. Figure* array[]={new Circle(1),new Circle (2),new Sphere(1)}; 8. // ISP (Interface Segregation Principle) fail 9. for(Figure *figure:array) std::cout << "Square:" << figure->Square() << std::endl; 10. for(Figure *figure:array) std::cout << "Volume:" << figure->Volume() << std::endl;
  • 27. Используем  tuple Example68_AbstractionLeak3 1. tuple<Circle,Circle,Sphere,Sphere> 2. t(Circle(1),Circle(2),Sphere(1),Sphere(2)); 3.// в параметры get<size_t> можно подставить только константу 4. std::cout << "Square:" << get<0>(t).Square() << std::endl; 5. std::cout << "Volume:" << get<3>(t).Volume() << std::endl;