Бублик Володимир Васильович Програмування - 2 Лекція 8. Процедурне програмування. Функції  Лекції для студентів 2 курсу
1. Визначення та виклик функції
Визначення функції <ul><li>//Greatest Common Divider </li></ul><ul><li>// заголовок </li></ul><ul><li>int  gcd (int m, int...
Виклик функції <ul><li>int  a, b, c; </li></ul><ul><li>cin>>a>>b; </li></ul><ul><li>c =  gcd (a, b); // 1. Ініціалізація п...
Способи виконання функцій <ul><li>Закрита функція </li></ul><ul><li>Викликається командою переходу з поверненням </li></ul...
Вбудовані функції <ul><li>Об'єктний код  закритої  функції присутній в програмі  один  раз. Відповідні команди керування п...
Приблизна схема викликів <ul><li>z= max (5, 6); </li></ul><ul><li>z= max(max(1,2),3); </li></ul><ul><li>double  x = 5; </l...
Макровизначення <ul><li>Макровизначення на перший погляд дуже схожі на вбудовані функції, вони теж підставляються в місця ...
Приклади макропідстановок <ul><li>double  a = MAX(1,2); //((1)>(2))?(1):(2) </li></ul><ul><li>cout<<a<<endl; //2 </li></ul...
Виклик функції <ul><li>inline double  max( double  x,  double  y) </li></ul><ul><li>{  return  (x>y)?x:y; } </li></ul><ul>...
2. Передача параметрів
Передача параметрів <ul><li>Розглянемо оголошення функції </li></ul><ul><li>void  f   ( T  x); </li></ul><ul><li>де  f  ― ...
Передача параметрів значенням <ul><li>Ініціалізація </li></ul><ul><li>T x  =  e ; </li></ul><ul><li>говорить про те, що пе...
Сторонній ефект <ul><li>Якщо передаються значення базових типів або структур, то виконання функції не впливає на наступні ...
Приклад 1. swap <ul><li>void  swap(  double  x,  double  y) </li></ul><ul><li>{ // значення базового типу  double </li></u...
Приклад  2 .  p swap <ul><li>void   p swap( double   * x,  double   * y) </li></ul><ul><li>{ </li></ul><ul><li>double   z ...
Приклад  3 .  r swap  ( тільки С++) <ul><li>void   r swap(  double   & x,  double   & y) </li></ul><ul><li>{ </li></ul><ul...
Безпечність виклику <ul><li>Безпечні </li></ul><ul><li>swap( a,b); r swap( a,b); </li></ul><ul><li>Небезпечний </li></ul><...
Масиви як параметри <ul><li>Масиви  ― головна причина використання параметрів-указників </li></ul><ul><li>Використання пар...
Задача додому <ul><li>Що б це значило? Організуйте виклик функції ptrswap та графічно зобразіть роботу з пам'яттю </li></u...
Приклад 4. <ul><li>Не має значення, як визначити параметр в кожній з трьох сигнатур </li></ul><ul><li>void  init   ( char ...
Приклад 4 (1). <ul><li>const int  n=26; </li></ul><ul><li>int main () </li></ul><ul><li>{ </li></ul><ul><li>char  ch[n]; <...
Приклад 4 ( 2 ) . <ul><li>void  init( char * ch) </li></ul><ul><li>{ </li></ul><ul><li>for (int i= 'a'; i<'a'+ n ; i++)   ...
Приклад 4 ( 3 ) . <ul><li>Допустимий навіть зовсім безглуздий виклик </li></ul><ul><li>char  ch; </li></ul><ul><li>init( &...
Приклад 4 ’ (1).  <ul><li>Краще рішення   передати розмір масиву додатковим параметром </li></ul><ul><li>void  init   ( ch...
Приклад 4 ’ (2). <ul><li>int main () </li></ul><ul><li>{ </li></ul><ul><li>const int n=26; </li></ul><ul><li>char  ch[n]; ...
Приклад 4 ’(2) . <ul><li>void  init   ( char * ch , size_t n ) </li></ul><ul><li>{ </li></ul><ul><li>for ( int  i= 'a'; i<...
Приклад 4 ’(3) . <ul><li>void  show( char  ch[n], size_t   n) </li></ul><ul><li>{ </li></ul><ul><li>cout<<&quot;start   st...
Приклад 4 ’(4) . <ul><li>void  swapar( char  ch[], size_t n) </li></ul><ul><li>{ </li></ul><ul><li>for  ( int  i = 0; i< n...
Приклад 4 ’( 5 ) . <ul><li>Результат роботи </li></ul><ul><li>s tart   string : </li></ul><ul><li>a b c d e f g h i j k l ...
Висновок <ul><li>Передаючи в функцію параметром масив (або указник як масив) ,  передбачаємо розмір масиву додатковим пара...
“ Загорнуті” масиви <ul><li>// Визначення “загорнутого” вектора </li></ul><ul><li>struct   WrappedVector </li></ul><ul><li...
Параметри сталі відсилки <ul><li>// сигнатура добутку (краще) </li></ul><ul><li>// структури не копіюються </li></ul><ul><...
Скалярний добуток <ul><li>// визначення статичного поля   структури </li></ul><ul><li>const int  WrappedVector::n = 100; <...
Створення вектору  ―  конструктор <ul><li>// Відсилка  ―  повний доступ </li></ul><ul><li>void   construct ( WrappedVector...
Видалення об'єкту  ― д еструктор <ul><li>void  destroy (Wrapped Vector &  a ) </li></ul><ul><li>{ </li></ul><ul><li>delete...
Замовчувані значення параметрів <ul><li>T f (T1 x1=a1, T2 x2=a2, …, Tn xn=an); </li></ul><ul><li>f(); f(c1); f(c1,c2);…. <...
Типи параметрів (перший підсумок) <ul><li>const T </li></ul><ul><li>T </li></ul><ul><li>T& </li></ul><ul><li>const T& </li...
Порівняння типів параметрів <ul><li>Найнадійніший  параметр-результат T& </li></ul><ul><li>Параметри-значення </li></ul><u...
Сталий параметр <ul><li>//Greatest Common Divider </li></ul><ul><li>int  gcd ( const  int  m 0 ,  const  int  n 0 ) </li><...
Параметри-указники <ul><li>T*   </li></ul><ul><li>const T* </li></ul><ul><li>T* const </li></ul><ul><li>const T*const </li...
Головна проблема параметра-указника Як відрізнити масив від скалярного значення? Вихід: жорстка дисципліна програмування, ...
Типова помилка в параметрі-указнику <ul><li>// Думалось, що ця функція виділятиме пам’ять для </li></ul><ul><li>// указник...
Результат <ul><li>// Виклик функції  allocate   </li></ul><ul><li>char  a[]=“This was before”; </li></ul><ul><li>cout<<a<<...
Параметри-указники <ul><li>T** </li></ul><ul><li>T*& </li></ul><ul><li>. . . . . . . . . . . . . . . . . </li></ul><ul><li...
Указник другого рівня <ul><li>// Тепер правильно   </li></ul><ul><li>void  allocate ( char  ** t  size_t  n,  char  c) </l...
Виклик указником другого рівня <ul><li>// Виклик функції  allocate  </li></ul><ul><li>char   * a=“This was before”; </li><...
Псевдонім указника <ul><li>void  allocate ( char  * & t,  size_t  n,  char  c) </li></ul><ul><li>{ </li></ul><ul><li>t =  ...
Виклик відсилкою до указника <ul><li>// Виклик функції  allocate   </li></ul><ul><li>char   * a=“This was before”; </li></...
Особливості нульового параметру-указника <ul><li>Структура дерева </li></ul><ul><li>struct  Tree  </li></ul><ul><li>{ </li...
Вершина дерева <ul><li>Параметр  aTree  відіграє роль параметру-результату </li></ul><ul><li>void  createTree (Tree  ** aT...
Видалення дерева <ul><li>void  destroyTree(Tree **aTree) </li></ul><ul><li>{ </li></ul><ul><li>Tree *subTree; </li></ul><u...
Приклад використання <ul><li>int main () </li></ul><ul><li>{ </li></ul><ul><li>Tree * t, * t1, *t2 ; </li></ul><ul><li>cre...
Завданння <ul><li>Переписати обробку дерев, замінивши скрізь, де можна указники відсилками </li></ul>
Указники  vs.  відсилки <ul><li>Замінимо </li></ul><ul><li>void  createTree (Tree **aTree,  int  node, Tree *left, Tree *r...
Зведення типів   при передачі параметрів <ul><li>void  show ( doubl e  x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<e...
Неявне зведення відсилок   неможливе <ul><li>void  show ( double   &  x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<en...
Явне зведення відсилок   хибне <ul><li>void  show ( double   &  x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<endl; </...
Неявне зведення указників   неможливе теж <ul><li>void  show ( double   *  x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<...
3. Обчислення результату
Результат функції <ul><li>Результати значення </li></ul><ul><li>T f(…); </li></ul><ul><li>const  T f(…); </li></ul><ul><li...
Результати-значення <ul><li>Типи результатів  T  і  const  T  не різняться для типів даних, прийнятих в С ( далі в С++ буд...
Точка площини <ul><li>struct  Point </li></ul><ul><li>{  </li></ul><ul><li>double _x; double _y; </li></ul><ul><li>}; </li...
Дерево <ul><li>Як і в попередньому прикладі результат повністю копіюється при виході з функції </li></ul><ul><li>Tree crea...
Результати-відсилки <ul><li>Дають повний доступ до частин агрегатів даних, але не забувайте про  правильну передачу  парам...
Результати   і параметри-відсилки <ul><li>Тепер повний доступ до параметру </li></ul><ul><li>double & x (Point   &  u)  </...
Сталі відсилки <ul><li>Результати не копіюються, але й не доступні для змін </li></ul><ul><li>const double & x ( const  Po...
Результати-указники <ul><li>Tree* createTree ( int  node, Tree *left, Tree *right) </li></ul><ul><li>{ </li></ul><ul><li>T...
Висновки <ul><li>Результат, створений у тілі функції ,  передаємо значенням </li></ul><ul><li>Доступ до частини або всього...
Рекурсія <ul><li>Перетворює визначення в програму </li></ul><ul><li>int  gcd ( int  v1,  int  v2) </li></ul><ul><li>{ </li...
Неефективність рекурсії <ul><li>int  BadFib ( int  n ) </li></ul><ul><li>{ </li></ul><ul><li>// Так не варто рахувати!!! <...
Ефективна рекурсія <ul><li>void  fib   ( int  &f1,  int  &f2,  int  n) </li></ul><ul><li>{ </li></ul><ul><li>// n  зменшує...
Upcoming SlideShare
Loading in...5
×

08 Functions

411

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
411
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

08 Functions

  1. 1. Бублик Володимир Васильович Програмування - 2 Лекція 8. Процедурне програмування. Функції Лекції для студентів 2 курсу
  2. 2. 1. Визначення та виклик функції
  3. 3. Визначення функції <ul><li>//Greatest Common Divider </li></ul><ul><li>// заголовок </li></ul><ul><li>int gcd (int m, int n) </li></ul><ul><li>//тіло функції </li></ul><ul><li>{ </li></ul><ul><li>while (m != n) </li></ul><ul><li>if ( m>n ) m=m-n; else n=n-m; </li></ul><ul><li>// m == n </li></ul><ul><li>//результат </li></ul><ul><li>return m; </li></ul><ul><li>} </li></ul>
  4. 4. Виклик функції <ul><li>int a, b, c; </li></ul><ul><li>cin>>a>>b; </li></ul><ul><li>c = gcd (a, b); // 1. Ініціалізація параметрів </li></ul><ul><li>// int m=a; int n=b; </li></ul><ul><li>//2. Виконання тіла фукції </li></ul><ul><li>//3. Обчислення результату (c=m;) </li></ul><ul><li>Звичайно виклик функції полягає в припиненні нормального ходу виконання об'єктного коду, що містить виклик. Відбувається ініціалізація і починає виконуватись об'єктний код функції. Після повернення результату виконання основного коду відновлюється </li></ul>
  5. 5. Способи виконання функцій <ul><li>Закрита функція </li></ul><ul><li>Викликається командою переходу з поверненням </li></ul><ul><li>Відкрита (вбудована) функція </li></ul><ul><li>Підставляється в кожне місце виклику </li></ul><ul><li>В обох випадках параметри і результат передаються в один і той же спосіб </li></ul>
  6. 6. Вбудовані функції <ul><li>Об'єктний код закритої функції присутній в програмі один раз. Відповідні команди керування приводять до її активації, виконання коду і наступного повернення в код виклику. Це накладні витрати виклику . Їх можна зменшити, якщо розміщувати код функції безпосередньо в місці виклику. Накладними витратами в цьому разі стане дублювання коду в кожному місці виклику. Компілятор сам приймає рішення, як реалізувати функцію ( inline лише порада). </li></ul><ul><li>inline double max( double x, double y) </li></ul><ul><li>{ return (x>y)?x:y; } </li></ul>
  7. 7. Приблизна схема викликів <ul><li>z= max (5, 6); </li></ul><ul><li>z= max(max(1,2),3); </li></ul><ul><li>double x = 5; </li></ul><ul><li>double y = 6; </li></ul><ul><li>z = (x>y)?x:y; </li></ul><ul><li>double x = 1; </li></ul><ul><li>double y = 2; </li></ul><ul><li>double x1 = (x>y)?x:y ; </li></ul><ul><li>double y1 = 3; </li></ul><ul><li>z = (x1>y1)?x1:y1 ; </li></ul>
  8. 8. Макровизначення <ul><li>Макровизначення на перший погляд дуже схожі на вбудовані функції, вони теж підставляються в місця виклику, але відрізняються від вбудованих функцій способом передачі параметрів текстовою підстановкою. </li></ul><ul><li>Приклад макровизначення </li></ul><ul><li># define MAX(x,y) ((x)>(y))?(x):(y) </li></ul><ul><li>Приклади макровикликів </li></ul><ul><li>MAX(1,2); </li></ul><ul><li>MAX(a+ b , c-d ); </li></ul>
  9. 9. Приклади макропідстановок <ul><li>double a = MAX(1,2); //((1)>(2))?(1):(2) </li></ul><ul><li>cout<<a<<endl; //2 </li></ul><ul><li>double b=2; </li></ul><ul><li>a= MAX(++a,b); // (( ++ a )>(b))?( ++ a ):(b) or </li></ul><ul><li>// (3>2)?4:2) == 4 </li></ul><ul><li>cout<<a<<endl; // 4 </li></ul><ul><li>// The next expression is not correct </li></ul><ul><li>// because of priorities </li></ul><ul><li>cout<<MAX(++a,b)<<endl; //means </li></ul><ul><li>// cout<< (5>2)?6:4 <<endl or </li></ul><ul><li>// (cout<<(5>2))?6:(4<<endl ) ERROR </li></ul>
  10. 10. Виклик функції <ul><li>inline double max( double x, double y) </li></ul><ul><li>{ return (x>y)?x:y; } </li></ul><ul><li>double a = max (1,2); </li></ul><ul><li>// double x=1; double y=2; // ініціалізація параметрів </li></ul><ul><li>// return (1>2)?1:2 == 2 виконання тіла і повернення </li></ul><ul><li>cout<<a<<endl; //2 </li></ul><ul><li>double b=2; </li></ul><ul><li>a= max (++a,b); </li></ul><ul><li>// double x=++a, double y=b; (x==3, y==2) </li></ul><ul><li>// return (3>2)?3:2 == 3 </li></ul>
  11. 11. 2. Передача параметрів
  12. 12. Передача параметрів <ul><li>Розглянемо оголошення функції </li></ul><ul><li>void f ( T x); </li></ul><ul><li>де f ― ім'я функції, T ― тип параметру, x ― ім'я параметру </li></ul><ul><li>Розглянемо виклик цієї функції </li></ul><ul><li>f(e) ; </li></ul><ul><li>де e ― вираз підходящого типу (фактичний параметр) </li></ul><ul><li>Виконання функції починається визначенням локальної змінної для параметру x з її одночасною ініціалізацією </li></ul><ul><li>T x = e ; </li></ul>
  13. 13. Передача параметрів значенням <ul><li>Ініціалізація </li></ul><ul><li>T x = e ; </li></ul><ul><li>говорить про те, що перед виконанням функції обчислюється значення фактичного параметру е ( r-value) </li></ul><ul><li>Кажуть, що параметри передаються значенням </li></ul><ul><li>Залежно від типу T ці значення можуть бути </li></ul><ul><li>значеннями базових типів, структур, </li></ul><ul><li>адресами (у випадку указників) </li></ul><ul><li>відсилками (у випадку псевдонімів) </li></ul>
  14. 14. Сторонній ефект <ul><li>Якщо передаються значення базових типів або структур, то виконання функції не впливає на наступні значення фактичних параметрів </li></ul><ul><li>Якщо формальний параметр є указником, то можлива зміна значення елементу пам'яті, на який цей указник встановлено </li></ul><ul><li>Якщо формальний параметр псевдонім, то через нього функція одержує прямий доступ до елементу пам'яті, до якого псевдонім відсилає </li></ul><ul><li>Якби не було стороннього ефекту, то який сенс мали б функції виду void f (T x) ? </li></ul>
  15. 15. Приклад 1. swap <ul><li>void swap( double x, double y) </li></ul><ul><li>{ // значення базового типу double </li></ul><ul><li>double z = x; x=y; y=z; </li></ul><ul><li>cout<<x<<‘,’<<y<<endl; //2,1 </li></ul><ul><li>} </li></ul><ul><li>double a=1, b=2; </li></ul><ul><li>swap( a,b); </li></ul><ul><li>// double x=a, y=b; </li></ul><ul><li>cout<<a<<‘,‘<<b<<endl; // 1,2 </li></ul>
  16. 16. Приклад 2 . p swap <ul><li>void p swap( double * x, double * y) </li></ul><ul><li>{ </li></ul><ul><li>double z = * x; * x= * y; * y=z; </li></ul><ul><li>cout<<*x<<‘,’<<*y<<endl; //2,1 </li></ul><ul><li>} </li></ul><ul><li>double a=1, b=2; </li></ul><ul><li>p swap( & a, & b); // за коректність відповідає користувач </li></ul><ul><li>// double *x= &a, *y= &b; </li></ul><ul><li>cout<<a<<‘,‘<<b<<endl; // 2,1 </li></ul>
  17. 17. Приклад 3 . r swap ( тільки С++) <ul><li>void r swap( double & x, double & y) </li></ul><ul><li>{ </li></ul><ul><li>double z = x; x=y; y=z; </li></ul><ul><li>cout<<x<<‘,’<<y<<endl; // 2,1 </li></ul><ul><li>} </li></ul><ul><li>double a=1, b=2; </li></ul><ul><li>r swap( a,b); // за коректність відповідає розробник </li></ul><ul><li>// double & x=a, & y=b; </li></ul><ul><li>cout<<a<<‘,‘<<b<<endl; // 2, 1 </li></ul>
  18. 18. Безпечність виклику <ul><li>Безпечні </li></ul><ul><li>swap( a,b); r swap( a,b); </li></ul><ul><li>Небезпечний </li></ul><ul><li>p swap( &a, &b); // p swap( 0, &b); ??? </li></ul><ul><li>void pswap(double *x, double *y) </li></ul><ul><li>{ </li></ul><ul><li>assert((x!=0)&&(y!=0)); </li></ul><ul><li>double z = *x; *x=*y; *y=z; </li></ul><ul><li>cout<<*x<<','<<*y<<endl; </li></ul><ul><li>} </li></ul>
  19. 19. Масиви як параметри <ul><li>Масиви ― головна причина використання параметрів-указників </li></ul><ul><li>Використання параметром масиву нічим не відрізняється від параметра-указника </li></ul><ul><li>T *a </li></ul><ul><li>T a[] </li></ul>
  20. 20. Задача додому <ul><li>Що б це значило? Організуйте виклик функції ptrswap та графічно зобразіть роботу з пам'яттю </li></ul><ul><li>void ptrswap( int *&u, int *&v ) </li></ul><ul><li>{ </li></ul><ul><li>int *w = v; </li></ul><ul><li>v = u; </li></ul><ul><li>u = w; </li></ul><ul><li>} </li></ul>
  21. 21. Приклад 4. <ul><li>Не має значення, як визначити параметр в кожній з трьох сигнатур </li></ul><ul><li>void init ( char * ch); </li></ul><ul><li>void show ( char ch[]); </li></ul><ul><li>void swapar ( char []); </li></ul>
  22. 22. Приклад 4 (1). <ul><li>const int n=26; </li></ul><ul><li>int main () </li></ul><ul><li>{ </li></ul><ul><li>char ch[n]; </li></ul><ul><li>init (ch); </li></ul><ul><li>show(ch); </li></ul><ul><li>swapar(ch); </li></ul><ul><li>show(ch); </li></ul><ul><li>return 0; </li></ul><ul><li>} </li></ul>
  23. 23. Приклад 4 ( 2 ) . <ul><li>void init( char * ch) </li></ul><ul><li>{ </li></ul><ul><li>for (int i= 'a'; i<'a'+ n ; i++) // не дуже добре </li></ul><ul><li>ch[i-'a'] = i; </li></ul><ul><li>} </li></ul><ul><li>Ніхто не заборонить навіть таке </li></ul><ul><li>for (int i= 'a'; i<'a'+ 100000 ; i++) </li></ul><ul><li>ch[i-'a'] = i; </li></ul><ul><li>Результат передбачити неможливо, швидше всього access violation </li></ul>
  24. 24. Приклад 4 ( 3 ) . <ul><li>Допустимий навіть зовсім безглуздий виклик </li></ul><ul><li>char ch; </li></ul><ul><li>init( & ch) ; </li></ul><ul><li>Теж access violation </li></ul>
  25. 25. Приклад 4 ’ (1). <ul><li>Краще рішення передати розмір масиву додатковим параметром </li></ul><ul><li>void init ( char * ch, int n ); </li></ul><ul><li>void show ( char ch[], int n ); </li></ul><ul><li>void swapar ( char [], int ); </li></ul>
  26. 26. Приклад 4 ’ (2). <ul><li>int main () </li></ul><ul><li>{ </li></ul><ul><li>const int n=26; </li></ul><ul><li>char ch[n]; </li></ul><ul><li>init (ch, n ); </li></ul><ul><li>show (ch, n ); </li></ul><ul><li>swapar (ch, n ); </li></ul><ul><li>show (ch, n ); </li></ul><ul><li>return 0; </li></ul><ul><li>} </li></ul>
  27. 27. Приклад 4 ’(2) . <ul><li>void init ( char * ch , size_t n ) </li></ul><ul><li>{ </li></ul><ul><li>for ( int i= 'a'; i<'a'+ n ; i++) //тепер в порядку </li></ul><ul><li>ch [ i-'a' ] = i; </li></ul><ul><li>} </li></ul>
  28. 28. Приклад 4 ’(3) . <ul><li>void show( char ch[n], size_t n) </li></ul><ul><li>{ </li></ul><ul><li>cout<<&quot;start string:&quot;<<endl; </li></ul><ul><li>for ( int i=0; i< n ; i++) </li></ul><ul><li>cout<<ch[i]<<' '; </li></ul><ul><li>cout<<endl; </li></ul><ul><li>} </li></ul>
  29. 29. Приклад 4 ’(4) . <ul><li>void swapar( char ch[], size_t n) </li></ul><ul><li>{ </li></ul><ul><li>for ( int i = 0; i< n /2; i++) </li></ul><ul><li>pswap(ch +i ,ch+n-1-i); </li></ul><ul><li>}; </li></ul>
  30. 30. Приклад 4 ’( 5 ) . <ul><li>Результат роботи </li></ul><ul><li>s tart string : </li></ul><ul><li>a b c d e f g h i j k l m n o p q r s t u v w x y z </li></ul><ul><li>s tart string : </li></ul><ul><li>z y x w v u t s r q p o n m l k j i h g f e d c b a </li></ul>
  31. 31. Висновок <ul><li>Передаючи в функцію параметром масив (або указник як масив) , передбачаємо розмір масиву додатковим параметром </li></ul><ul><li>void f (T *array, size_t size); </li></ul><ul><li>або краще </li></ul><ul><li>void f (T [] array, size_t size); </li></ul>
  32. 32. “ Загорнуті” масиви <ul><li>// Визначення “загорнутого” вектора </li></ul><ul><li>struct WrappedVector </li></ul><ul><li>{ </li></ul><ul><li>static const int n; // статичне поле одне на всіх </li></ul><ul><li>double * x; </li></ul><ul><li>}; </li></ul><ul><li>// сигнатура добутку (дуже погана) </li></ul><ul><li>double prod ( WrappedVector , WrappedVector ); </li></ul>
  33. 33. Параметри сталі відсилки <ul><li>// сигнатура добутку (краще) </li></ul><ul><li>// структури не копіюються </li></ul><ul><li>double prod ( WrappedVector & , WrappedVector & ); </li></ul><ul><li>// сигнатура добутку (ще краще) </li></ul><ul><li>// структури не копіюються, </li></ul><ul><li>// доступ для зміни фактичного </li></ul><ul><li>// параметру закрито </li></ul><ul><li>double prod ( const WrappedVector & , const WrappedVector & ); </li></ul>
  34. 34. Скалярний добуток <ul><li>// визначення статичного поля структури </li></ul><ul><li>const int WrappedVector::n = 100; </li></ul><ul><li>// визначення скалярного добутку </li></ul><ul><li>double prod ( const WrappedVector& a, const WrappedVector& b) </li></ul><ul><li>{ </li></ul><ul><li>double s = 0; </li></ul><ul><li>for ( int i=0; i<a.n; i++) </li></ul><ul><li>s+=a.x[i]*b.x[i]; </li></ul><ul><li>return s; </li></ul><ul><li>} </li></ul>
  35. 35. Створення вектору ― конструктор <ul><li>// Відсилка ― повний доступ </li></ul><ul><li>void construct ( WrappedVector & ); </li></ul><ul><li>void construct (WrappedVector & a) </li></ul><ul><li>{ </li></ul><ul><li>a.x = new double [a.n]; </li></ul><ul><li>for ( int i=0; i<a.n; i++) </li></ul><ul><li>a.x[i] = rand(); </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  36. 36. Видалення об'єкту ― д еструктор <ul><li>void destroy (Wrapped Vector & a ) </li></ul><ul><li>{ </li></ul><ul><li>delete [] a.x; </li></ul><ul><li>a.x = 0; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul><ul><li>// Як примусити кожного прибирати за собою? </li></ul>
  37. 37. Замовчувані значення параметрів <ul><li>T f (T1 x1=a1, T2 x2=a2, …, Tn xn=an); </li></ul><ul><li>f(); f(c1); f(c1,c2);…. </li></ul><ul><li>void createPoint(Point & u, double x=0, double y=0) </li></ul><ul><li>{ </li></ul><ul><li>u._x = x; u._y=y; </li></ul><ul><li>} </li></ul><ul><li>createPoint (u); // (0,0) </li></ul><ul><li>createPoint (v, 1); // (1,0) </li></ul><ul><li>createPoint (w, 1, 2); // (1,2) </li></ul>
  38. 38. Типи параметрів (перший підсумок) <ul><li>const T </li></ul><ul><li>T </li></ul><ul><li>T& </li></ul><ul><li>const T& </li></ul><ul><li>Копіювання, незмінний формальний параметр , немає впливу на фактичний параметр </li></ul><ul><li>Копіювання, змінний формальний параметр, немає впливу на фактичний параметр </li></ul><ul><li>Відсилка без копіювання, повний доступ до фактичного параметру (C++) </li></ul><ul><li>Стала відсилка без копіювання, фактичний параметр незмінний (C++) </li></ul>
  39. 39. Порівняння типів параметрів <ul><li>Найнадійніший параметр-результат T& </li></ul><ul><li>Параметри-значення </li></ul><ul><li>Точка зору користувача: параметри T , const T і const T& не відрізняються з точки зору використання </li></ul><ul><li>Точка зору розробника: const T надійніший, бо не створює непорозумінь; </li></ul><ul><li>const T& найефективніший, оскільки не вимагає витрат на копіювання </li></ul>
  40. 40. Сталий параметр <ul><li>//Greatest Common Divider </li></ul><ul><li>int gcd ( const int m 0 , const int n 0 ) </li></ul><ul><li>{ </li></ul><ul><li>int m = m0, n = n0; </li></ul><ul><li>while (m != n) </li></ul><ul><li>if ( m>n ) m=m-n; else n=n-m; </li></ul><ul><li>// m == n </li></ul><ul><li>// Початкові значення не втрачено </li></ul><ul><li>assert((m0%m==0) && (n0%n==0) ); </li></ul><ul><li>return m; </li></ul><ul><li>} </li></ul>
  41. 41. Параметри-указники <ul><li>T* </li></ul><ul><li>const T* </li></ul><ul><li>T* const </li></ul><ul><li>const T*const </li></ul>Повний але непрямий доступ до фактичного параметру; передача масивів ( T[]) (в стилі чистого С) Маловживаний тип параметру; доступ лише для читання: сталий фактичний параметр ( vs const T&) Незмінний формальний параметр - указник , немає впливу на фактичний параметр-адресу ( this в C++) Незмінний указник на сталий фактичний параметр В усіх випадках 5-8 сам указник передається значенням
  42. 42. Головна проблема параметра-указника Як відрізнити масив від скалярного значення? Вихід: жорстка дисципліна програмування, додатковий параметр типу size_t з відповідним коментарем void f ( double * px, size_t size_of_px );
  43. 43. Типова помилка в параметрі-указнику <ul><li>// Думалось, що ця функція виділятиме пам’ять для </li></ul><ul><li>// указника t </li></ul><ul><li>void allocate ( char *t, size_t n, char c) </li></ul><ul><li>{ </li></ul><ul><li>t = new char [n]; </li></ul><ul><li>t[n-1]=''; </li></ul><ul><li>for (int i=0;i<n-1;i++) </li></ul><ul><li>t[i]=c; </li></ul><ul><li>cout<<“This is inside: “<<t<<endl; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  44. 44. Результат <ul><li>// Виклик функції allocate </li></ul><ul><li>char a[]=“This was before”; </li></ul><ul><li>cout<<a<<endl; </li></ul><ul><li>// This was before </li></ul><ul><li>allocate (a, 10 , ‘a’); </li></ul><ul><li>// This is inside: aaaaaaaaaa </li></ul><ul><li>cout<<a<<endl; </li></ul><ul><li>// This was before </li></ul><ul><li>// Вихід: передати указник відсилкою </li></ul><ul><li>void construct ( WrappedVector & ); </li></ul>
  45. 45. Параметри-указники <ul><li>T** </li></ul><ul><li>T*& </li></ul><ul><li>. . . . . . . . . . . . . . . . . </li></ul><ul><li>const T* const * const </li></ul>Указник-вихідний параметр (чистий С) Те ж саме, але в стилі С++ А ще різні варіації на тему const
  46. 46. Указник другого рівня <ul><li>// Тепер правильно </li></ul><ul><li>void allocate ( char ** t size_t n, char c) </li></ul><ul><li>{ </li></ul><ul><li>* t = new char [n]; </li></ul><ul><li>(*t) [n-1]=''; </li></ul><ul><li>for ( int i=0;i<n-1;i++) </li></ul><ul><li>(*t) [i]=c; </li></ul><ul><li>cout<< *t <<endl; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  47. 47. Виклик указником другого рівня <ul><li>// Виклик функції allocate </li></ul><ul><li>char * a=“This was before”; </li></ul><ul><li>cout<<a<<endl; </li></ul><ul><li>// This was before </li></ul><ul><li>allocate ( & a, 10 , ‘a’); </li></ul><ul><li>// This is inside: aaaaaaaaaa </li></ul><ul><li>cout<<a<<endl; </li></ul><ul><li>// aaaaaaaaaa </li></ul>
  48. 48. Псевдонім указника <ul><li>void allocate ( char * & t, size_t n, char c) </li></ul><ul><li>{ </li></ul><ul><li>t = new char [n]; </li></ul><ul><li>t[n-1]=''; </li></ul><ul><li>for ( int i=0;i<n-1;i++) </li></ul><ul><li>t[i]=c; </li></ul><ul><li>cout<<t<<endl; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  49. 49. Виклик відсилкою до указника <ul><li>// Виклик функції allocate </li></ul><ul><li>char * a=“This was before”; </li></ul><ul><li>cout<<a<<endl; </li></ul><ul><li>// This was before </li></ul><ul><li>allocate (a, 10 , ‘a’); </li></ul><ul><li>// This is inside: aaaaaaaaaa </li></ul><ul><li>cout<<a<<endl; </li></ul><ul><li>// aaaaaaaaaa </li></ul>
  50. 50. Особливості нульового параметру-указника <ul><li>Структура дерева </li></ul><ul><li>struct Tree </li></ul><ul><li>{ </li></ul><ul><li>int node; </li></ul><ul><li>Tree *left; </li></ul><ul><li>Tree *right; </li></ul><ul><li>}; </li></ul><ul><li>Вузли-листя мають нульові указники left і right </li></ul>
  51. 51. Вершина дерева <ul><li>Параметр aTree відіграє роль параметру-результату </li></ul><ul><li>void createTree (Tree ** aTree, int node, Tree *left, Tree *right) </li></ul><ul><li>{ </li></ul><ul><li>*aTree = new Tree; </li></ul><ul><li>(*aTree) -> node = node; </li></ul><ul><li>(*aTree) -> left = left; </li></ul><ul><li>(*aTree) -> right = right; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  52. 52. Видалення дерева <ul><li>void destroyTree(Tree **aTree) </li></ul><ul><li>{ </li></ul><ul><li>Tree *subTree; </li></ul><ul><li>if ((*aTree)==0) return ; </li></ul><ul><li>subTree = (*aTree)->left; //left subtree </li></ul><ul><li>destroyTree(&subTree); </li></ul><ul><li>subTree = (*aTree)->right; //right subtree </li></ul><ul><li>destroyTree(&subTree); </li></ul><ul><li>delete *aTree; //delete root </li></ul><ul><li>*aTree = 0; </li></ul><ul><li>return ; </li></ul><ul><li>} </li></ul>
  53. 53. Приклад використання <ul><li>int main () </li></ul><ul><li>{ </li></ul><ul><li>Tree * t, * t1, *t2 ; </li></ul><ul><li>createTree(&t1,1,0,0); </li></ul><ul><li>createTree(&t2,2,0,0); </li></ul><ul><li>createTree(&t,3,t1,t2); </li></ul><ul><li>destroyTree (&t); // Проблема: значення t1 і t2 </li></ul><ul><li>return 0; </li></ul><ul><li>} </li></ul>
  54. 54. Завданння <ul><li>Переписати обробку дерев, замінивши скрізь, де можна указники відсилками </li></ul>
  55. 55. Указники vs. відсилки <ul><li>Замінимо </li></ul><ul><li>void createTree (Tree **aTree, int node, Tree *left, Tree *right); </li></ul><ul><li>на </li></ul><ul><li>void createTree (Tree * & aTree, int node, Tree *left, Tree *right); </li></ul><ul><li>але не </li></ul><ul><li>void createTree (Tree * & aTree, int node, Tree &left, Tree &right ); </li></ul><ul><li>бо виклик createTree(t1,1,0,0); стане незаконним </li></ul>
  56. 56. Зведення типів при передачі параметрів <ul><li>void show ( doubl e x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<endl; </li></ul><ul><li>} </li></ul><ul><li>// OK </li></ul><ul><li>int k=1; show(k); //1 </li></ul><ul><li>long int lk=2; show(lk); //2 </li></ul><ul><li>float x=3; show(x); //3 </li></ul>
  57. 57. Неявне зведення відсилок неможливе <ul><li>void show ( double & x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<endl; </li></ul><ul><li>} </li></ul><ul><li>// ERRORS </li></ul><ul><li>int k=1; show(k); </li></ul><ul><li>long int lk=2; show(lk); </li></ul><ul><li>float x=3; show(x); </li></ul>
  58. 58. Явне зведення відсилок хибне <ul><li>void show ( double & x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<endl; </li></ul><ul><li>} </li></ul><ul><li>// Type conversion </li></ul><ul><li>int k=1; show(( double &) k); //-9.25596e+061 </li></ul><ul><li>long int lk=2; show(( double &) lk); //2.122e-314 </li></ul><ul><li>float x=3; show(( double &) x); //4.77656e-314 </li></ul><ul><li>Не намагайтеся допомогти компілятору: не вийде! </li></ul>
  59. 59. Неявне зведення указників неможливе теж <ul><li>void show ( double * x) </li></ul><ul><li>{ </li></ul><ul><li>cout<<x<<endl; </li></ul><ul><li>} </li></ul><ul><li>// ERRORS </li></ul><ul><li>int k=1; show(&k); </li></ul><ul><li>long int lk=2; show(&lk); </li></ul><ul><li>float x=3; show(&x); </li></ul>
  60. 60. 3. Обчислення результату
  61. 61. Результат функції <ul><li>Результати значення </li></ul><ul><li>T f(…); </li></ul><ul><li>const T f(…); </li></ul><ul><li>Результати відсилки </li></ul><ul><li>T& f(…); </li></ul><ul><li>const T& f(…); </li></ul><ul><li>Результати указники </li></ul><ul><li>T* f(…): </li></ul>
  62. 62. Результати-значення <ul><li>Типи результатів T і const T не різняться для типів даних, прийнятих в С ( далі в С++ будуть відрізнятися ) </li></ul>
  63. 63. Точка площини <ul><li>struct Point </li></ul><ul><li>{ </li></ul><ul><li>double _x; double _y; </li></ul><ul><li>}; </li></ul><ul><li>const Point plus (Point u, Point v) </li></ul><ul><li>{ </li></ul><ul><li>Point res; </li></ul><ul><li>res._x = u._x+v._x; </li></ul><ul><li>res._y = u._y+v._y; </li></ul><ul><li>return res; </li></ul><ul><li>} </li></ul>
  64. 64. Дерево <ul><li>Як і в попередньому прикладі результат повністю копіюється при виході з функції </li></ul><ul><li>Tree createTree ( int node, Tree * left, Tree * right) </li></ul><ul><li>{ </li></ul><ul><li>Tree aTree; </li></ul><ul><li>aTree.node = node; </li></ul><ul><li>aTree.left = left; </li></ul><ul><li>aTree.right = right; </li></ul><ul><li>return aTree ; </li></ul><ul><li>} </li></ul>
  65. 65. Результати-відсилки <ul><li>Дають повний доступ до частин агрегатів даних, але не забувайте про правильну передачу параметру </li></ul><ul><li>double & x (Point u) </li></ul><ul><li>{ </li></ul><ul><li> return u._x; </li></ul><ul><li>} </li></ul><ul><li>Point v; v._x = 10; v._y = 20; </li></ul><ul><li>cout<<x(v) <<endl; //10 </li></ul><ul><li>x(v)= 125; </li></ul><ul><li>cout<< x(v)<<endl; //10 , а не 125 ?! </li></ul>
  66. 66. Результати і параметри-відсилки <ul><li>Тепер повний доступ до параметру </li></ul><ul><li>double & x (Point & u) </li></ul><ul><li>{ </li></ul><ul><li> return u._x; </li></ul><ul><li>} </li></ul><ul><li>Point v; v._x = 10; v._y = 20; </li></ul><ul><li>cout<< x(v) <<endl; //10 </li></ul><ul><li>x(v)= 125; </li></ul><ul><li>cout<< x(v) <<endl; // 125 , а не 10?! </li></ul>
  67. 67. Сталі відсилки <ul><li>Результати не копіюються, але й не доступні для змін </li></ul><ul><li>const double & x ( const Point & u) </li></ul><ul><li>{ </li></ul><ul><li> return u._x; </li></ul><ul><li>} </li></ul><ul><li>Point v; v._x = 10; v._y = 20; </li></ul><ul><li>cout<< x(v) <<endl; //10 </li></ul><ul><li>// x(v)= 125; not possible now </li></ul>
  68. 68. Результати-указники <ul><li>Tree* createTree ( int node, Tree *left, Tree *right) </li></ul><ul><li>{ </li></ul><ul><li>Tree * aTree = new Tree; </li></ul><ul><li>aTree->node = node; </li></ul><ul><li>aTree->left = left; </li></ul><ul><li>aTree->right = right; </li></ul><ul><li>return aTree; </li></ul><ul><li>} </li></ul><ul><li>Тепер можлива суперпозиція </li></ul><ul><li>t = createTree (3, createTree (1,0,0), createTree (2,0,0)); </li></ul>
  69. 69. Висновки <ul><li>Результат, створений у тілі функції , передаємо значенням </li></ul><ul><li>Доступ до частини або всього агрегату даних, переданого фактичним параметром, забезпечуємо результатом відсилкою </li></ul><ul><li>Створені у функції агрегати даних передаються указником </li></ul>
  70. 70. Рекурсія <ul><li>Перетворює визначення в програму </li></ul><ul><li>int gcd ( int v1, int v2) </li></ul><ul><li>{ </li></ul><ul><li>return v2 ? gcd (v2, v1%v2) : v1; </li></ul><ul><li>} </li></ul>
  71. 71. Неефективність рекурсії <ul><li>int BadFib ( int n ) </li></ul><ul><li>{ </li></ul><ul><li>// Так не варто рахувати!!! </li></ul><ul><li>if (n==0) return 0; </li></ul><ul><li>if (n==1) return 1; </li></ul><ul><li>return BadFib(n-1)+BadFib(n-2); </li></ul><ul><li>} </li></ul>
  72. 72. Ефективна рекурсія <ul><li>void fib ( int &f1, int &f2, int n) </li></ul><ul><li>{ </li></ul><ul><li>// n зменшується на 1, f1 і f2 зсуваються вправо </li></ul><ul><li>int f; </li></ul><ul><li>if (n>=2) </li></ul><ul><li>{ </li></ul><ul><li>f=f2; f2+=f1; f1=f; </li></ul><ul><li>fib(f1, f2, n-1); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×