8. Класове

План:
Използване на класове и обекти
Обособяване на класове
Капсулиране на данните
Член-функции
Конструктори по подразбиране и с параметри
Достъп до полетата с данни
Сравняване на член-функции с обикновени функции


** Използване на класове и обекти
В лекциите до тук за класовете са разгледани:
- конструиране на обект: Employee harry("Hacher, Harry", 350);
- модифициране на обект: harry.set_salary(380);
- извеждане на информация за обект: cout << harry.get_name();
Въведени са и основните понятия конструктор, член-функция и операция точка.


** Обособяване на класове.
Ще разгледаме задача, която се решава без използване на клас и след това с дефиниране и използване на подходящ клас.
Пример: Чете се информация за компютри, съдържаща:
име на модел name OmniBook XE
цена price 5660
оценка score 76
OmniBook XE
5660
76
ACMA P300
1095
75
AMAX Poewrstation
1999
78
Търси се продукта с най-голямо отношение оценка/цена.
Първо програмно решение - без използване на класове.
// bestval.cpp
#include <iostream>
#include <string>
using namespace std;

int main()
{  string best_name = "";    // име
   double best_price = 0;    // цена
   int best_score = 0;       // оценка
   bool more = true;
   while (more)
   {  string next_name;
      double next_price;
      int next_score;
      cout << "Please enter the model name: ";
      getline(cin, next_name);
      cout << "Please enter the price: ";
      cin >> next_price;
      cout << "Please enter the score: ";
      cin >> next_score;
      string remainder; /* read remainder of line */
      getline(cin, remainder);
      if (next_price != 0)
      {  if (best_price == 0 or
            next_score/next_price > best_score/best_price)
         {  best_name = next_name;
            best_price = next_price;
                       best_score = next_score;
         }

      }
      cout << "More data? (y/n) ";
      string answer;
      getline(cin, answer);
      if (answer != "y") more = false;
   }
   cout << "The best bang for the buck is " << best_name
      << " (Score: " << best_score
      << " Price: " << best_price << ")\n";
   return 0;
}

Множествата от променливи за име, цена и оценка описват един продукт и можем да ги обединим в един клас.

** Интерфейс и капсулиране, членове-функции.
Интерфейсът на класа се състои от всички членове-функции, които ще се прилагат за обектите от този клас.

Второ програмно решение на задачата с използване на класове.
// product2.cpp
#include <iostream>
#include <string>
using namespace std;

/* ДЕФИНИЦИЯ НА КЛАСА */
class Product  {
public:                 /* интерфейс, открита част */
/* декларации на член-функциите на класа */
   Product();                        /* създава нов продукт */
   void read();                      /* чете продукт        */
   bool is_better_than(Product b) const;/* сравнява продукти */
   void print() const;               /* отпечатва продукт   */
private:               /* капсулиране, скрита част */
   string name;                      /* скрити данни */
   double price;                     /* скрити данни */
   int score;                        /* скрити данни */
};
/* ИЗПОЛЗВАНЕ НА КЛАСА */
int main()
{  Product best;       /* дефиниране на обект от класа */
   bool more = true;
   while (more)
   {  Product next;
      next.read();    /* извикване на член-функция на класа */
      if (next.is_better_than(best)) best = next;
      cout << "More data? (y/n) ";
      string answer;
      getline(cin, answer);
      if (answer != "y") more = false;
   }
   cout << "The best bang for the buck is ";
   best.print();        /* извикване на член-функция на класа */
   return 0;
}
/* РЕАЛИЗАЦИЯ НА КЛАСА */
/* дефиниция на конструктор */
Product::Product()
{ price = 10000;
  score = 0;
}
/* дефиниция на член-функция мутатор (set-функция) */
void Product::read()
{  cout << "Please enter the model name: ";
   getline(cin, name);
   cout << "Please enter the price: ";
   cin >> price;
   cout << "Please enter the score: ";
   cin >> score;
   string remainder;
   getline(cin, remainder);
}
/* дефиниция на член-функции за достъп (get-функция) */
bool Product::is_better_than(Product b) const
{  if (price == 0) return false;
    if (b.price == 0) return true;
    if (score/price > b.score/b.price) return true;
    return false;
}
/* дефиниция на член-функции за достъп (get-функция) */
void Product::print() const
{ cout << name
       << " Price: " << price
       << " Score: " << score << "\n";
}
/* КРАЙ на файла, съдържащ текста на програмата */ 

- set-функции (мутатори) - променят (стойността на) неявния си параметър;
    void read();
- get-функции (функции за достъп) - не променят (стойността на) неявния си параметър, могат да се обявят като константни функции.
    void print() const;  


** Капсулиране на данните
- две секции: публична (public), частна (private)
- само конструкторът на класа и член-функциите имат достъп до частните членове на класа (данните)
- скриването на данните (от външни функции) се нарича капсулиране (encapsulation)
- капсулацията налага индиректно използване на данните - чрез публични член-функции, което предпазва от повреда на данните.
Пример:
class Time {
public:
Time();
Time(int h, int m, int s);
void add_seconds(long s);

long seconds_from(Time t) const;
int get_seconds() const;
int get_minutes() const;
int get_hours() const;
private:
int hours; /* conjecture */
int minutes; /* conjecture */
int seconds; /* conjecture */
};
Ако имаше пряк достъп до данните, то може да се получи невалидно време:
Time liftoff(19, 30, 0);
/* liftoff is delayed by six hours */
/* won't compile, but suppose it did */
liftoff.hours = liftoff.hours + 6;
Получава се 25:30:00!
В класа има функция (add_seconds), която добавя секунди и пресмята вярно часа, минутите и секундите.

** Член-функции
Неявен и явен параметри на член-функция
bool Product::is_better_than(Product b) const
{ if (b.price == 0) return false;
if (price == 0) return true;
return score / price > b.score / b.price;
}
if (next.is_better_than(best)) ...

** Конструктори по подразбиране и с параметри.
* Конструкторът инициализира (задава начални стойности на) данните на класа. Няма тип и не връща стойност.
* Предефиниране на функции - две функции в една програма на С++ могат да имат еднакви имена.
За да бъдат различими, те трябва да имат различен брой параметри или/и параметрите им да са от различен тип.
class Employee {
public:
   Employee(); 
                                /* конструктор без параметри */
   Employee(string emp_name, double init_salary);
/* конструктор с параметри */
   void set_salary(double new_salary);
   string get_name() const;
   double get_salary() const;
   void print() const;
private:
   string name;
   double salary;
};
/* дефиниция на конструктора с параметри */
Employee::Employee(string emp_name, double init_salary)
{ name = emp_name;
  salary = init_salary; 
}

* Полета с данни - обекти от други класове.
class Employee {
public:
   Employee(string n, double sal, int arr, int leav);
   ...
private:
   string name;
   double salary;
   Time arrive;
   Time leave;
};

Employee::Employee(string n, double sal, int arr, int leav)
{ name = n;
  salary = sal;
  arrive = Time(arr, 0, 0);
  leave = Time(leav, 0, 0);
}

Класът Employee съдържа  членове-данни, които са обекти от клас Time.


** Достъп до полетата с данни.
* Само членовете-функции имат достъп до скритите полета с данни.
    string Employee::get_name() const
    { return name;
    }
    void Employee::set_salary(double new_salary)
    { salary = new_salary;
    }

Представянето на данните на класа е скрито от потребителя на класа (на интерфейса).
Пример: Класът Time, написан от Кай Хорстман.

class Time {
public:
Time();
Time(int h, int m, int s);
void add_seconds(long s);

long seconds_from(Time t) const;
int get_seconds() const;
int get_minutes() const;
int get_hours() const;
private:
long time_in_secs;
};

Реализациите (дефинициите) на член-функциите също са скрити за потребителя на класа.
Пример:

Time::Time(int hour, int min, int sec)
{ time_in_secs = 60 * 60 * hour + 60 * min + sec;
}

int Time::get_minutes() const
{ return (time_in_secs / 60) % 60;
}

int Time::seconds_from(Time t) const
{ return time_in_secs - t.time_in_secs;
}

* Сравняване на член-функции с обикновени функции.
Отпечатване на името и заплатата на служител, реализирано с три различни функции:
-- член-функция с използване на достъп до скритите данни:
   void Employee::print() const
   { cout << "Name: " << name << "    "
          << "Salary: " << salary << "\n";
   }

-- член-функция с използване на член-функции от същия клас:
   void Employee::print() const
   { cout << "Name: " << get_name() << "    "
          << "Salary: " << get_salary() << "\n";
   }

-- външна за класа функция:
   void print(const Employee &emp)
   { cout << "Name: " << emp.get_name() << "    "
          << "Salary: " << emp.get_salary() << "\n";
   } 

Пример: Разширена реализация на клас Employee.

// employee.cpp
#include <iostream>
using namespace std;
#include <ccc_time.h>

class Employee {
public:
   Employee(string employee_name, double initial_salary,
            int arrive_hour, int leave_hour);

   void set_salary(double new_salary);
   void raise_salary(double percent);

   string get_name() const;
   double get_salary() const;
   void print() const;
private:
   string name;
   double salary;
   Time arrive;
   Time leave;

};

Employee::Employee(string employee_name, double initial_salary,
   int arrive_hour, int leave_hour)
{  name = employee_name;
   salary = initial_salary;
   arrive = Time(arrive_hour, 0, 0);
   leave = Time(leave_hour, 0, 0);

}

void Employee::set_salary(double new_salary)
{  salary = new_salary; 
}    

string Employee::get_name() const
{  return name;
}

double Employee::get_salary() const
{  return salary;
}

void Employee::raise_salary(double percent)
{  salary = salary * (1 + percent / 100);
}


void raise_salary(Employee& e, double percent)
{  double new_salary = e.get_salary() * (1 + percent / 100);
   e.set_salary(new_salary);
}


void Employee::print() const
{  cout << "Name: " << get_name() << "; "
        << "Salary: " << get_salary() << "; "
    << "Arrive: " << arrive.get_hour() << "; "
    << "Leave: " << leave.get_hour() << "\n";
}


void print(const Employee &emp)
{ cout << "Name: " << emp.get_name() << "    "
    << "Salary: " << emp.get_salary() << "\n";
}


int main()
{ Employee harry("Harry", 1000, 8, 16);
  harry.raise_salary(10);
  harry.print();
  raise_salary(harry, 10);
  cout << "New salary " << harry.get_salary() << endl;
  print(harry);
  return 0;  
}   




Явен (explicit) параметър Неявен (implicit) параметър
параметър-променлива
(не се променя)
По подразбиране
Пример:

void print(Employee);
print(harry); //
параметър harry
Използва се const
Пример:
void Employee::print() const;
harry.print();
// параметър harry
параметър-псевдоним
(може да се промени)

Използва се &
Пример:
void raiseSalary(Employee& e, double p);
raiseSalary(harry, 10); // параметър harry

По подразбиране
Пример:
void Employee::raiseSalary(double p);
harry.
raiseSalary(10);// параметър harry


** Разделяне програмата на:
-- заглавен файл (product.h),
-- дефиниции на функциите на класа (product.cpp),
-- използване на обекти от класа (prodtest.cpp).
Разделна компилация.