2. Обекти, параметри на фукции, динамична памет

  
Конструиране на обекти.
    Обектът е стойност, която може да се създава, запазва и обработва със средствата на езика за програмиране. Обект = променлива или константа, като понятието обект се използва за "по-големи" променливи или константи. Например
 int k = 1;               /* k е обект, по-добре променлива */
 string greeting = "Hello"; /* greeting и "Hello" са обекти */
 * Тип данни клас. Класовете се дефинират от програмиста. Класът Time е дефиниран от К. Хорстман. Обект от този клас ще съхранява момент от времето - час, минути, секунди.
 * Конструиране на обект. Time(23,59,59) означава 23 часа, 59 минути и 59 секунди. Това е обект-константа, аналогичен на числовите константи (числата) и низовете, напр. 6, 2.3,  "Hello".
 * Включване на файл с дефиниция на класа:
    #include "ccc_time.h"
 * Дефиниране на променлива (обект) от тип Time:
        Time day_end = Time(23,59,59);
или по-кратко (съкратен запис):
    Time day_end(23,59,59);
Името на променливата е day_end, типът на променливата е Time и началната стойност на променливата е Time(23,59,59).
 * Конструктор и параметри на конструктора.
   За създаване на обекти от клас се използва функция, наречена конструктор на класа:
-- името на конструктора съвпада с името на класа;
-- параметрите на конструктора са зададени в дефиницията на класа.
 * Конструиране по подразбиране.
   Задаване на текущото време по компютърния часовник (в момента на изпълнение на програмата):
       Time now = Time();
или съкратено:
   Time now;

Използване на обекти.
* Член-функции на класа:
   Time wake_up(7,0,0);       /* 7:00:00 */
   wake_up.add_seconds(1000); /* 7:16:40 */
Добавя 1000 секунди към момента от време, записан в обекта wake_up.
 * Функции на класа Time:
 
 Име  Действие
Time() Конструира обект, показващ текущото време
Time(h,m,s) Конструира обект, показващ h часа, m минути и s секунди
t.get_seconds() Връща броя на секундите в t.
t.get_minutes() Връща броя на минутите в t.
t.get_hours() Връща броя на часовете в t.
t.add_seconds(n) Премества t с n секунди напред.
t.seconds_from(t2)  Пресмята броя на секундите между t  и t2.

Колко секунди остават до края на деня?
// time2.cpp
#include <iostream> 

#include "ccc_time.h"
using namespace std;

int main()
{ Time now;
  Time day_end(23, 59, 59);
  long seconds_left = day_end.seconds_from(now);
  cout << "There are " << seconds_left
      << " seconds left in this day.\n";
  return 0;
}

There are 44219 seconds left in this day.

     Тъй като денонощието съдържа 24x3600 = 86400 секунди, член-функцията seconds_from връща стойност от тип long.


Обекти от реалния свят.
    Обект от тип Employee (служител) ще съхранява име на служител и неговата (годишна) заплата. В програмата се създава обект harry с начална заплата 45000 и след това му се повишава заплатата с още 3000.
// employee.cpp
#include <iostream>
#include "ccc_empl.h"
using namespace std;

int main()
{  Employee harry("Hacker, Harry", 45000.00);
   double new_salary = harry.get_salary() + 3000;
   harry.set_salary(new_salary);
   cout << "Name: " << harry.get_name() << endl;
   cout << "Salary: " << harry.get_salary() << endl;
   return 0;
}

Name: Hacker, Harry
Salary: 48000

В таблицата са дадени всички член-функции, дефинирани в класа Employee:
 

Име Действие
Employee(n,s) Конструира обект, представящ служител с име n и заплата s.
e.get_name() Връща името на e.
e.get_salary() Връща заплатата на e.
e.set_salary(s)  Дава нова стойност s на заплатата на e.

Параметри на функции. 
** Формални параметри  - параметри-променливи, задават се при дефиниране на функцията. 
double future_value(double initial_balance, double p, int nyear) 
** Фактически параметри - представляват изрази за инициализация на параметрите-променливи, указват се при извикване на функцията. 
b = future_value(total/2, rate, year1-year2); 
    Фактическите параметри съответстват по тип и брой на формалните параметри. В този пример променливите total и rate са от тип double, а year1 и year2 са от тип int . 


 Деклариране на функции. 
    Функциите трябва да бъдат известни, преди да бъдат използвани. Дефинирането на дадена функция преди да бъде използвана не винаги е удобно, особено при по-големи програми, които съдържат много функции. За да използваме (извикаме) една функция достатъчно е са известни типа и името на функцията и типовете на параметрите й. Тези данни се съдържат в декларацията на функцията, която се състои от: тип на функцията (на връщаната стойност), име на функцията, типове и имена на параметри, точка и запетая.
    В езика C++ файла с текста на програмата се подрежда най-често по следния начин:  декларация на функция, използване на функция, дефиниране на функция. Декларациите на стандартните за езика функции са в заглавните файлове. 
#include <iostream> 
#include <cmath> 
using namespace std;

/* декларация на функцията future_value */ 
double future_value(double initial_balance, double p, int nyear); 
int main() 
{ ... 
/* използване на функцията future_value */ 
 double bal = future_value(10000, rate, 10); 
 ... 
} 
/* дефиниция на функцията future_value */ 
double future_value(double initial_balance, double p, int nyear) 
{ 
 ... 
} 


Странични ефекти и процедури. 
** Външно забележимият ефект от функция се нарича страничен ефект - отпечатване на някакво съобщение, прекратяване работата на програмата и др. Функциите по принцип не трябва да имат странични ефекти. 
** Когато едно действие трябва да бъде извършено многократно (поне 2 пъти), има смисъл да бъде обособено като функция. Функция, която не връща стойност се нарича процедура. 
* Процедура за отпечатване на обект от тип Time. 
// printtime.cpp 
#include <iostream> 
#include <iomanip>
#include "ccc_time.h"
using namespace std; 

void print_time(Time t) 
/* ЦЕЛ:  отпечатва времето във формат hh:mm:ss 
   ПОЛУЧАВА: t - времето, което ще се отпечати 
*/ 
{  cout << t.get_hours() << ":"; 
   if (t.get_minutes() < 10) cout << "0"; 
   cout << t.get_minutes() << ":"; 
   if (t.get_seconds() < 10) cout << "0"; 
   cout << t.get_seconds(); 
} 
int main() 
{  Time liftoff(7, 0, 15); 
   cout << "Liftoff: "; print_time(liftoff); cout << "\n"; 
   Time now; 
   cout << "Now: "; print_time(now); cout << "\n"; 
   return 0; 
}


Параметри-псевдоними. 

* Параметър-псевдоним не представлява нова променлива, а друго име (псевдоним) на съществуваща променлива в извикващата функция. 
* Процедура raise_salary, която увеличава заплатата на служител с by процента. 
//reisesal.cpp 
#include <iostream>
#include "ccc_empl.h"

using namespace std; 

void raise_salary(Employee& e, double by) 
/* ЦЕЛ:  увеличава заплатата на даден служител 
   RECEIVES: e - служител, получаващ увеличение 
             by - процент на увеличението 
*/ 
{  double new_salary = e.get_salary() * (1 + by / 100); 
   e.set_salary(new_salary); 
} 
int main() 
{  Employee harry("Hacker, Harry", 45000.00); 
   raise_salary(harry, 5); 
   cout << "New salary: " << harry.get_salary() << "\n"; 
   return 0; 
}

New salary: 47250

    Формалният параметър Employee& e на функцията raise_salary е параметър-псевдоним на променливата harry от главната функция main.Промяната на стойността на параметъра-псевдоним e във функцията raise_salary води до промяна и на стойността на фактическия параметър (променливата) harry от функция main. Затова параметрите-псевдоними се наричат още входно-изходни параметри - те "пренасят" информация от извикващата функция към извиканата функция и обратно.

Константни псевдоними. 
    Използват се за да се избегне копирането - особено нежелателно, ако параметърът е обект, съдържащ много данни. Тъй като стойността на константния псевдоним не може да се променя, той не "пренася" информация от извиканата функция в извикващата функция. 
void print_employee(Employee const & e) 
{ 
 cout << "Name:   " << e.get_name() 
      << "Salary: " << e.get_salary(); 
} 

параметри-променливи параметри-псевдоними константни псевдоними
входни параметри за функцията входно-изходни параметри за функцията -
предаване на параметри чрез копиране предаване на параметри по адрес предаване на параметри по адрес
стойността на параметъра може да се променя стойността на параметъра може да се променя стойността на параметъра не може да се променя
фактически параметър може да бъде константа, променлива или израз фактически параметър може да бъде само променлива фактически параметър може да бъде константа, променлива или израз

 
Указатели и динамична памет. 

** Дялове в оперативната памет при изпълнение на програма на С++. 

CODE 
транслираната програма 
(изпълним файл)
DATA 
глобални данни
STACK 
локални променливи и константи 
имена на извикани функции
Динамична памет  (heap)

** Тип указател - съхранява адрес от оперативната памет на компютъра. 
- дефиниране на променлива тип указател: 
синтаксис: име на тип * име на променлива 
Employee *boss; 
int *pn;

- запазване на място в динамичната памет - унарна операция new: 
синтаксис: 
new име на тип - запазва място за една променлива от зададения тип 
new име на тип(стойност)- запазва място за една променлива от зададения тип и я инициалира със зададената стойност 
new име на тип[число]- запазва място за няколко променливи (колкото е числото) от зададения тип.

- дефиниране на променлива тип указател и задаване на начална стойност на указателя: 
int *pn = new int; 
int *arr = new int[100];

- дефиниране на променлива тип указател (boss), задаване на начална стойност на указателя (операция new) и задаване на начална стойност на сочения от него обект: 
Employee *boss = new Employ("John", 3200); 
int *pn = new int(10);

STACK 
 
Име: boss
Тип: Employee* (указател)
Стойност: адрес XXX

 
Име: pn
Тип: int* (указател)
Стойност: адрес YYY
Динамична памет 

адрес XXX

Име: *boss (временно!)
Тип: Employee
Стойност:
"John"
3200

адрес YYY

Име: *pn (временно!)
Тип: int
Стойност: 10

- стойност на указател - унарна операция *: 
синтаксис: *указател 
Employee harry("Harry", 1500); 
*boss = harry; 
*pn = 12; 
cout << *pn;

- операция стрелка ->: 
синтаксис: указател към клас -> член на класа 
cout << (*boss).get_salary(); 
cout << boss->get_salary();

- освобождаване на памет (унищожаване на обекта) - операция delete. 
синтаксис: delete указател 
delete boss; 
delete pn;

- невалидни указатели. 
int *pk = new int(10); 
cout << *pk; 
delete pk; 
*pk = 100; /* невалиден указател */ 
Опасна грешка! Опит за работа със стойност на указател след освобождаване на заетата памет.

- операция адрес на променлива &. 
синтаксис: & име на променлива

int k = 10; 
int *pk = &k; 
cout << k; 
cout << (*pk);
STACK 
Адрес XXX
Име: k
Тип: int
Стойност: 10
STACK 
 
Име: pk
Тип: int* (указател)
Стойност: Адрес XXX

- променливи-псевдоними. 
int k = 10; 
int &ak = k; /* дефиниране на променлива-псевдоним */ 
if ( ak == k ) cout << "YES, of course";


** Указатели и масиви, адресна аритметика. 
    Името на масив е константен указател. 
int a[3] = {10, 20, 30}; 
int *pa = a; 
/* отпечатва 3 пъти стойността на a[0] */ 
cout << a[0] <<" "<< pa[0] <<" "<< *pa; 
/* отпечатва 4 пъти стойността на a[1] */ 
cout << a[1] <<" "<< pa[1] <<" "<< *(pa+1) <<" "<<(pa+1)[0]; 
Предефиниране на бинарна операция + за първи аргумент адрес и втори аргумент цяло число.

Адрес pa или a pa+1 или a+1 pa+2 или a+2
Стойност 10 20 30
Индекс 0 1 2
Достъп 1 a[0] a[1] a[2]
Достъп 2 *pa *(pa+1) *(pa+2)
Достъп 3 pa[0] (pa+1)[0] (pa+1)[1]
Достъп 4 *a *(a+1) *(a+2)