ინკაფსულაცია

  • ახალი მონაცემთა ტიპის შექმნა სტრუქტურის გამოყენებით
  • ინკაპსულაცია. კლასის ცნება
  • კლასის მონაცემებზე წვდომა (private,  public). კლასის ობიექტი
  • კონსტრუქტორი. კონსტრუქტორის გადატვირთვა
  • დესტრუქტორი. კონსტრუქტორის და დესტრუქტორის

გამოძახების დრო და რიგი

ახალი მონაცემთა ტიპის შექმნა სტრუქტურის გამოყენებით

C++-ში ახალი მონაცემთა ტიპის შექმნა შესაძლებელია სტრუქტურის საფუძველზე. შემდეგ მაგალითში მონაცემთა ტიპი Room იქმნება სტრუქტურის სახით, რომელიც შეიცავს ორ  height  და walls  ელემენტს. პროგრამაში შემოიღება Room ტიპის MyRoom ცვლადი. მის ველებს ენიჭებათ მნიშვნელობები, ხოლო  ფუნქცია  aboutRoom  ბეჭდავს ინფორმაციას ოთახის შესახებ.

#include <iostream>
using namespace std;
struct Room{
float height; //  სიმაღლე
int walls;    // კედლების რიცხვი
};

void aboutRoom(Room &);
int main(){
Room MyRoom;
cout<<"Data :\n";
aboutRoom(MyRoom);
MyRoom.height =3.5;
MyRoom.walls =4;
cout<<"Correct data :\n";
aboutRoom(MyRoom);
MyRoom.height =300;
MyRoom.walls =119;
cout<<"Incorrect data :\n";
aboutRoom(MyRoom);
system("pause");
return 0;

}

void aboutRoom(Room& R) {
cout<<"My Room has "<<R.walls

       <<" walls"<<endl

       <<"Height of my Room is "

       <<R.height<<endl<<endl;

}

      პროგრამის შესრულების შედეგად მივიღებთ:

Data :

My Room has 2 walls
Height of my Room is 5.60519e-044
Correct data :
My Room has 4 walls
Height of my Room is 3.5
Incorrect data :
My Room has 119 walls
Height of my Room is 300
Press any key to continue . . .

მაგალითში სტრუქტურა მოცემულია C -ს სტილში: მასში გაერთიანებულია მხოლოდ მონაცემები. ფუნქცია, რომელიც ამუშავებს ამ მონაცემებს, განსაზღვრულია სტრუქტურის გარეთ (არის გლობალური ფუნქცია). პროგრამის შედეგი გვიჩვენებს, რომ ახალი Room ტიპის შექმნას C-ს სტილის სტრუქტურის საფუძველზე აქვს რამოდენიმე უარყოფითი მხარე:

  • Room ტიპის ცვლადის საწყისი ინიციალიზება მისი შექმნის მომენტში გათვალისწინებული არ არის, ე.ი. სტრუქტურის ელემენტებს თავიდანვე შეიძლება მიენიჭოს ნებისმიერი მნიშვნელობები, მათ შორის, არაკორექტულიც;
  • თუ MyRoom ცვლადის ინიციალიზებას მოვახდენთ პროგრამაში, მაშინაც მის ელემენტებს შეიძლება მიენიჭოს არაკორექტური მნიშვნელობები. ეს ხდება იმიტომ, რომ სტრუქტურის ველებზე ნებადართულია პირდაპირი წვდომა პროგრამიდან, რაც წარმოადგენს მნიშვნელოვან შეფერხებას ახალი ტიპების შექმნისათვის სტრუქტურის გამოყენებით;
  • პროგრამაში არ არსებობს საშუალება, რომელიც აუკრძალავდა მომხმარებელს არასწორი მონაცემების შეტანას. ანუ არ არის გათვალისწინებული ”ინტერფეისი”, რომელიც გარანტიას იძლევა, რომ მომხმარებელი კორექტულად იყენებს მონაცემთა ტიპს და მონაცემები არ ”ფუჭდება”.

საზოგადოდ, ვერ ხერხდება სტრუქტურის ტიპის ცვლადის შესახებ ინფორმაციის გამოტანა მთლიანობაში, ანუ არ შეგვიძლია დავწეროთ  cout << MyRoom; - სტრუქტურის თითოეული ელემენტი უნდა გამოვიტანოთ ცალცალკე. ასევე ვერ ხერხდება სტრუქტურის ტიპის ცვლადების შედარება მთლიანობაში: ცვლადები უნდა შევადაროთ ცალკეული შესაბამისი ელემენტის მიხედვით და სხვა.   

ზემოთ ჩამოთვლილი  პრობლემების აცილება შეგვიძლია შემდეგი გზით:

  • შემოვიღოთ ფუნქცია, რომელიც მოახდენს ობიექტის ინიციალიზებას მისი შექმნის მომენტში. მართალია, შეიძლებოდა სტრუქტურის განაცხადშივე მოგვეხდინა მისი ველების ინიციალიზება, მაგალითად, ასე

Room MyRoom ={ 3.7, 4};

 მაგრამ ეს შეზღუდავდა პროგრამის ზოგადობას;

  • შემოვიღოთ ფუნქცია, რომელშიც შემოწმდება შეტანილი მონაცემების სისწორე;
  • აუკრძალოთ პროგრამას მონაცემების პირდაპირი გამოყენება (შემოვიღეთ მონაცემთა დაცვის მექანიზმი): მონაცემებზე წვდომისა­თვის შემოვიღოთ სპეციალური ფუნქციები როგორც მათი მნიშვნელობების შეცვლისათვის, ისე მათი მნიშვნელობების პროგრამაში გამოყენებისათვის;
  • განვსაზღვროთ Room  ტიპის ცვლადების შედარების ფუნქცია;
  • განვსაზღვროთ Room ტიპის ცვლადების << ოპერატორის გამოყენებით ბეჭდვის საშუალება

და სხვა.

 ზემოთთქმულის გათვალისწინებით, სტრუქტურაზე განაცხადს და ფუნქციების პროტოტიპებს შეიძლება ჰქონდეს შემდეგი სახე:

#include <iostream>
using namespace std;
struct Room{
float height;
int walls;

};

void init(Room& , float=3.5, int=4);
void check_data(Room &);
void set_height(Room& , float );
void set_walls(Room& , int );
int get_height(Room& );
int get_walls(Room& );
bool compare(Room &, Room &);
void aboutRoom(Room &);

თუ Room ტიპის აღწერაში დავამატებთ ველებს, მაგალითად windows, floor, ceiling და სხვა, ფუნქციების რიცხვი საგრძნობლად იმატებს.

ამოცანის სირთულის ზრდასთან ერთად იზრდება მონაცემთა დამუშავებისთვის საჭირო ფუნქციათა რიცხვი, რთულდება ამ ფუნქციების ურთიერთქმედებისა და მონაცემთა ცვლილების კონტროლი.

ამ პრობლემების გადასაწყვეტად C++ აფართოვებს სტრუქტურის ცნებას - შესაძლებელი ხდება მონაცემების და მათი დამუშავების ფუნქციების გაერთიანება სტრუქტურის აღწერაში.

Room ტიპზე დანაცხადს C++ -ში შესაძლოა ჰქონდეს სახე:

#include <iostream>
using namespace std;
struct Room{

       float height;
int walls;
void init(float=3, int=4);
void check_data( );
void set_height( float );
void set_walls( int );
float get_height ( );
int get_walls( );
bool compare(Room & );
void aboutRoom( );

};

მონაცემების და ფუნქციების გაერთიანებით სტრუქტურის აღწერაში მიიღწევა პროგრამული ობიექტის აღქმის გაიოლება, ფუნქციათა პროტოტიპების და კოდების გამარტივება, სახელების კონფლიქტის აცილება, დაპროგრამების პროცესისა და პროგრამის განახლების პროცესის მნიშვნელოვნად გაადვილება.

გულისხმობის პრინციპით სტრუქტურის ელემენტები ღია ( public ) წვდომისა არიან, ანუ სტრუქტურის მონაცემთა გამოყენება და შეცვლა შეუძლია პროგრამის ყველა ფუნქციას. ეს ხშირად შეცდომების წყაროს წარმოადგენს. მაგალითად, მომხმარებელმა უნებლიედ შეიძლება მიანიჭოს სტრუქტურის მონაცემებს არაკორექტული მნიშვნელობები. გარდა ამისა, სტრუქტურის ელემენტებს თავიდანვე (მისი შექმნის მომენტში) შეიძლება მიენიჭოს ნებისმიერი მნიშვნელობები, მათ შორის, არაკორექტულიც.

მონაცემთა  დაცვის მიზნით ავკრძალოთ პირდაპირი წვდომა  მათზე - გამოვიყენოთ მონაცემებზე წვდომის private (დახურული) მეთოდი:

#include <iostream>
using namespace std;
struct Room{

   // ფუნქციებზე პირდაპირი წვდომის ნებადართვა

       public:
void init(float=3, int=4);
void check_data( );
void set_height( float );
void set_walls( int );
float get_height ( );
int get_walls( );
bool compare(Room & );
void aboutRoom( );

// მონაცემებზე პირდაპირი წვდომის აკრძალვა:

// ახლა  MyRoom.walls =5; სახის ინსტრუქციები

   //  main-სა და სხვა ფუნქციებში აკრძალულია

       private:
float height;
int walls;

};

შეიძლება ითქვას, რომ C++ -ის სტრუქტურა აკმაყოფილებს ობიექტზე ორიენტირე­ბული დაპროგრამების (Object Oriented Programming - OOP) ინკაპსულაციის კონცეფციას.

ახალი აბსტრაქტული ტიპების შექმნისთვის C++ -ში შემოდის ახალი ცნება - კლასი.  C++ -ის სტრუქტურა და კლასი თითქმის არ განსხვავდებიან. მიუხედავათ ამისა, ახალი ტიპების შექმნა C++ -ში მიღებულია კლასის აღწერით, ხოლო სტრუქტურა, ჩვეულებრივ, გამოიყენება  C -ს სტილში.

ინკაპსულაცია.  კლასის ცნება

ობიექტზე ორიენტირებული დაპროგრამების ფუძემდებელი იდეა არის მონაცემებისა და მათზე გათვალისწინებული მოქმედებების გაერთიანება ე.წ. ობიექტში, პროგრამა კი წარმოადგენს ობიექტების ურთიერთქმედებას. OOP –ში რეალური სამყაროს ობიექტები წარმოდგენილია აბსტრაქტული მონაცემთა ტიპის - კლასის - საფუძველზე.

ერთი კლასის ობიექტებს აქვთ ერთი და იგივე ატრიბუტები და ამ ატრიბუტების დამუშავების წესები. C++ -ში ასეთი წესების როლს ასრულებენ ფუნქციები. სხვა სიტყვებით, ობიექტების თვისებებს აღწერენ კლასის მონაცემები, ხოლო ობიექტების ქცევა რეალიზებულია ფუნქციების სახით. მონაცემები და ფუნქციები მჭიდროდ არიან დაკავშირებული და განიხილებიან ერთიანობაში. C++ -ის ამ კონცეფციას  ეწოდება ინკაპსულაცია.

კლასის მონაცემებს ასევე ეწოდებათ წევრი მონაცემები, ხოლო კლასის ფუნქციებს - წევრი ფუნქციები.  ობიექტზე ორიენტირებულ სხვა ენებში კლასის ფუნქციებს უწოდებენ მეთოდებს. ტიპი, რომელიც ერთიანად აღწერს ობიექტის მონაცემებს და ფუნქციებს, C++ -ში განისაზღვრება მომსახურე სიტყვით class.

შემდეგ მაგალითში განხილულია იგივე Room ტიპი, მაგრამ არა როგორც სტრუქტურა, არამედ როგორც კლასი. ახსნის თვალსაზრისიდან გამომდინარე, განვიხილავთ Room ტიპის გამარტივებული ვერსია.

Room ტიპზე განაცხადი:

#include <iostream>
using namespace std;
class Room{

    public:
Room( );
void setData(float, int );
void aboutRoom( );
private:
float height;
int walls;

};

კლასის მონაცემებზე წვდომა (private, public). კლასის ობიექტი

public (ღია, საერთო) და private (დახურული, კერძო) ჭდეებს კლასის აღწერაში ეწოდებათ ელემენტებზე წვდომის სპეციფიკატორები. მონაცემებზე და ფუნქციებზე, რომელთა განაცხადები მოთავსებულია public სპეციფიკატორის შემდეგ ნებადართულია წვდომა პროგრამის ნებისმიერი ადგილიდან. private სპეციფიკატორის შემდეგ განცხადებულ მონაცემებზე და ფუნქციებზე პროგრამიდან წვდომა აკრძალულია. ასეთ  მონაცემებთან და ფუნქციებთან მუშაობა შეუძლიათ მხოლოდ ამავე კლასის ფუნქციებს (და ე. წ. მეგობარ ფუნქციებს). წვდომის სპეციფიკატორი აუცილებლად მთავრდება ” : ”-ით და კლასის აღწერაში შეიძლება გამეორდეს რამდენჯერმე ნებისმიერი მიმდევრობით. მაგალითად, სწორი იქნებოდა Room კლასზე შემდეგი განაცხადი:

#include <iostream>
using namespace std;
class Room{

    private:
float height;
int walls;

    public:
Room();
void setData(float, int );
void aboutRoom();   

};

თუ გავითვალისწინებთ, რომ კლასის მონაცემებიც და ფუნქციებიც გულისხმობის პრინციპით დახურული წევრებია, მაშინ ზემოთ მოყვანილ განაცხადში შეიძლება გამოვტოვოთ private სპეციფიკატორი:

#include <iostream>

using namespace std;

class Room{  

       float height;

       int walls;

    public:

       Room();

       void setData(float, int );

       void aboutRoom();   

};

ყურადღება მივაქციოთ იმას, რომ Room ტიპზე თავდაპირველ განაცხადში private სპეციფიკატორის გამოტოვება არ შეიძლება, რადგან წინააღმდეგ შემთხვევაში კლასის ყველა წევრი გახდება ღია წვდომისა.

კლასზე განაცხადის სტილი – პროგრამისტის გემოვნების საკითხია.

Room კლასის მონაცემები  height და  walls არიან კლასის დახურული ელემენტები და მათზე მოქმედება შეუძლიათ მხოლოდ კლასის ფუნქციებს  Room,  setData  და  aboutRoom.

Room კლასის  public განყოფილებაში მოცემულია სამი  -  Room,  setData და  aboutRoom  - ფუნქციის პროტოტიპი. ესენი - კლასის მომსახურების ღია ინტერფეისი ან კლასის ღია ფუნქციებია.  მათ იყენებენ კლასის კლიენტები (ე.ი. პროგრამის სხვა ფუნქციები) ამ კლასის მონაცემებზე მოქმედებისათვის. კლასის ფუქციების განსაზღვრა ხდება ე.წ. რეალიზების ნაწილში და უდნა იყოს მოთავსებული კლასის აღწერის გარეთ. უფრო მეტიც, რეალიზების ნაწილი შეიძლება იყოს გატანილი სხვა ფაილში.

Room ტიპის ცვლადზე განაცხადს შესაძლოა ჰქონდეს სახე

Room MyRoom;

კლასის ტიპის ცვლადს ეწოდება კლასის ეკზემპლარი ან კლასის ობიექტი. ჩვენს შემთხვევაში  MyRoom  არის   Room  კლასის ობიექტი.

კონსტრუქტორი. კონსტრუქტორის გადატვირთვა

პროგრამას, რომელიც იყენებს  Room კლასს შესაძლოა ჰქონდეს შემდეგი სახე:

#include <iostream>

using namespace std;

// კლასის აღწერა

class Room{  

    public:

       Room ();

       void setData (float, int );

       void aboutRoom();

    private:  

       float height;

       int walls;       

};

// კლასის რეალიზების ნაწილი  -  კლასის ფუნქციათა განსაზღვრა

Room::Room() {

    height =3.7;

    walls =4;

}

void Room:: setData(float h, int w){

    height  =h;

    walls =w;

}

void Room:: aboutRoom(){

     cout<<"My Room has "<< walls

            <<" walls "<<endl

            <<"Height of my Room is "

            <<height<<endl<<endl;

}

int main()

{

   Room MyRoom;

   cout<<"Data:\n";

   MyRoom.aboutRoom();

   MyRoom.setData(2.5, 7);

   cout<<endl<<"Data after change:\n";

   MyRoom.aboutRoom();

   system("pause");

   return 0;

}

              პროგრამის შესრულების შედეგია:

 Data:

My Room has 4 walls

Height of my Room is 3.5

Data after change:

My Room has 7 walls

Height of my Room is 2.5

Press any key to continue . . .

 

 

პროგრამის შედეგიდან ჩანს, რომ მომხმარებელმა მაინც „მოახერხა“ არაკორექტური მონაცემების გამოყენება. setData ფუნქციაში გავითვალისწინოთ მისი უპასუხისმგებლო ქმედების აკრძალვა, მაგალითად ასე:

void Room:: setData(float h, int w){

    if(h <= 2.5 || h >= 4) h =3.5;

    if(w != 4) w =4;

    height  =h;

    walls =w;

}

კლასის დახურულ height და walls მონაცემებზე კლასის გარეთ წვდომა აკრძალულია. ანუ main -ში ან პროგრამის სხვა ფუნქციაში არ შეიძლება იყოს                  MyRoom.height =2.85; -ის მსგავსი შეტყობინებები. ამბობენ, რომ კლასის რეალიზება დამალულია კლიენტებისაგან. ასეთ მიდგომას პრინციპული მნიშვნელობა აქვს: ინფორმაციის დამალვა ხელს უწყობს მონაცემების დაცვას და აიოლებს პროგრამის მოდიფიცირებას (საჭიროების შემთხვევაში შეგვიძლია რომელიმე ფუნქციის კოდი შევცვალოთ ისე, რომ პროგრამის დანარჩენ ნაწილებს არ შევეხოთ).

ფუნქცია Room(), რომლის სახელი ემთხვევა კლასის სახელს, არის კლასის კონსტრუქტორი. კონსტრუქტორი - სპეციალური ფუნქციაა, რომელიც ახდენს ობიექტის მონაცემების საწყის ინიციალიზებას და ავტომატურად გამოიძახება კლასის ყოველი ობიექტის შექმნის მომენტში. კონსტრუქტორს არ გააჩნია დასაბრუნებელი მნიშვნელობა.

რეალიზების ნაწილში განისაზღვრება კლასის ფუნქციები. მივაქციოთ ყურადღება ფუნქციის სათაურის სინტაქსს: 

void Room:: setData(float h, int w)

აქ  Room:: აღნიშნავს, რომ ფუნქცია ეკუთვნის Room კლასს (არის კლასის ხილვადობის არეში). ფუნქციის სათაურში  Room:: -ის გამოტოვება გამოიწვევს კომპილაციის შეცდომას.

     განვიხილოთ კიდევ ერთი მაგალითი. შემოვიღოთ მონაცემთა ტიპი, რომელიც შეესაბამება ცნებას  Time - დრო. მისი აღწერა მოვახდინოთ კლასის საშუალებით:

class Time {

          public:

             Time();

Time(int, int, int);

            void showTime();

          private:

              int hour;

            int minute;

            int second;

      };

კლასის მონაცემები - 3 მთელი რიცხვი hour, minute და second - აღნიშნავენ საათს, წუთსა და წამს და არიან კლასის დახურული (private) წევრები. hour, minute და second მონაცემებს მნიშვნელობები შეიძლება მიანიჭოს მხოლოდ კლასის ფუნქციამ (ან კლასის მეგობარმა ფუნქციამ). ამ ცვლადების საწყისი ინიციალიზება არ შეიძლება კლასის აღწერაში და არც პროგრამის სხვა ფუნქციაში.

მონაცემთა საწყისი ინიციალიზება სრულდება Time კონსტრუქტორის საშუალებით. კონსტრუქტორი აუცილებლად  უნდა იყოს კლასის ღია (public) ფუნქცია.

კონსტრუქტორი შეიძლება იყოს უპარამეტრო:

Time:: Time(){

      hour =12;   minute =25; second =40;

}  

ან პარამეტრებიანი:

Time:: Time(int h, int m, int s){

       hour =h;   minute =m; second =s;

}

კლასის აღწერაში დასაშვებია იყოს რამოდენიმე კონსტრუქტორი, მაგალითად, ზემოთ მოყვანილი ორივე კონსტრუქტორი. ამას ეწოდება კონსტრუქტორის გადატვირთვა.

ვთქვათ, Time კლასის ობიექტებზე განაცხადს აქვს სახე:             

Time t1, t2(3,12,30);

ახალი ობიექტის შექმნისას ავტომატურად გამოიძახება კონსტრუქტორი. მისი მეშვეობით ხდება ობიექტისათვის მეხსიერების განაწილება და ობიექტის მონაცემების ინიციალიზება. კომპილერი იძახებს კონსტრუქტორის საჭირო ეკზემპლარს ობიექტზე განაცხადის მიხედვით. t1 ობიექტისათვის გამოიძახება უპარამეტრო კონსტრუქტორი, რომელიც ჩაწერს  hour, minute და  second ველებში მნიშვნელობებს  12, 25  და  40.  t2(3,12,30) ობიექტისათვის გამოიძახება პარამეტრებიანი კონსტრუქტორი, რომელსაც გადაეცემა ობიექტის სახელის შემდეგ მრგვალ ფრჩხილებში მოცემული არგუმენტები და რომელიც მიანიჭებს მათ  t2 ობიექტის შესაბამის ველებს:  hour -ს მიენიჭება  3,  minute -ს   -  12,  second -ს  - 30.

უპარამეტრო და პარამეტრებიანი კონსტრუქტორები კლასის განაცხადში შეიძლება შევცვალოთ ერთი კონსტრუქტორით, რომლის ფორმალურ პარამეტრებს მინიჭებული აქვთ საწყისი მნიშვნელობები გაჩუმებით (ე.წ. კონსტრუქტორი გაჩუმებით):  

Time(int =12, int =25, int =40)

ასეთი კონსტრუქტორის განსაზღვრა რეალიზების ნაწილში უნდა გამოიყურებოდეს შემდეგნაირად:

Time:: Time(int h, int m, int s){

       hour =h;   minute =m; second =s;

}

კონსტრუქტორი გაჩუმებით უზრუნველყოფს როგორც t1, ისე  t2(3,12,30) ობიექტის სწორ ინიციალიზებას: t1 ობიექტის hour, minute და second მონაცემებს მიენიჭებათ მნიშვნელობები გაჩუმებით (12,25 და 40 შესაბამისად), ხოლო t2 ობიექტის შემთხვევაში მოხდება გაჩუმებით მნიშვნელობების ჩანაცვლება არგუმენტების მნიშვნელობებით 3, 12 და 30, ე.ი.  hour -ს მიენიჭება 3,  minute -ს - 12,  second -ს  - 30.

     დავამატოთ კლასში კიდევ ერთი კონსტრუქტორი Time(char* ), რომელსაც არგუმენტის სახით გადაეცემა დროის შემცველი სტრიქონი, მაგალითად, ”4-17-50”. კონსტრუქტორმა უნდა მიიღოს სტრიქონიდან და მიანიჭოს ობიექტის მონაცემებს 3 მთელი რიცხვი 4, 17 და 50:

Time:: Time(char* str){

        sscanf(str,"%d%*c%d%*c%d", &hour, &minute,&second);

}

აქ sscanf არის სტრიქონიდან ინფორმაციის წაკითხვის ფუნქცია. sscanf ფუნქციის პროტოტიპი მოცემულია   cstdio  თავსართ ფაილში.

ფუნქცია  showTime ბეჭდავს დროს სტანდარტულ 12-საათაინ ფორმატში.

Time კლასის აღწერა მიიღებს სახეს:

class Time {       

    public:

       Time (int=12, int=25, int=40);

       Time (char *);

        void showTime(); 

    private:

                         int hour;

             int minute;

             int second;

};

კონსტრუქტორი მუშაობს მხოლოდ ერთხელ - ობიექტის შექმნის მომენტში. იმისათვის, რომ შესაძლებელი იყოს ობიექტის მონაცემების შეცვლა პროგრამის შესრულების ნებისმიერ მომენტში, კლასში უნდა გვქონდეს სპეციალური ფუნქციები. თუ პროგრამაში გვჭირდება ობიექტის მონაცემების მნიშვნელობები, მათ ”ამოკითხვას” აგრეთვე ასრულებენ კლასის სპეციალური ფუნქციები. კლასის აღწერაში დავამატოთ კლასის private მონაცემებზე წვდომის ფუნქციები (ე.წ. ფუნქცია-უტილიტები) და განვიხილოთ საილუსტრაციო მაგალითი.

#include <iostream>

using namespace std;

class Time{

    public:

        Time(int =12, int =25, int =40);

        Time(char* );

        int get_h();

        int get_m();

        int get_s();

        void set_h(int);

        void set_m(int);

        void set_s(int);

        void showTime();

    private:

        int hour;

      int minute;

      int second;

};

// კლასის ფუნქციების განსაზღვრა

Time:: Time(int h, int m, int s){

   hour =h;  minute =m;  second =s;

}

Time:: Time(char* str){

   sscanf(str,"%d%*c%d%*c%d",

                 &hour,&minute,&second);

}

int Time::get_h(){

   return hour;

}

int Time::get_m(){

   return minute;   

}

int Time::get_s(){

   return second;   

}

void Time::set_h(int h){

    hour =h;

}

void Time::set_m(int m){

    minute =m;    

}

void Time::set_s(int s){

    second =s;    

}

void Time:: showTime(){

   cout<<((hour == 0 || hour == 12)? 12 : hour%12)

       <<":"<<(minute < 10 ? "0" : "" )<<minute

       <<":"<<(second < 10 ? "0" : "" )<<second

                 <<endl;

}

int main()

{

   Time t;

   Time st("4-17-50"), t1, t2(3,12,30);

     cout<<"String-Time ";   

     st.showTime();

     cout<<"t1 = ";   t1.showTime();

     cout<<"t2 = ";   t2.showTime();

     t1 =t2;  

     cout<<"After copy\nt1 = "; 

          t1.showTime();

   Time t3;

    t3.set_h(t1.get_h()); 

        t3.set_m(22); 

    t3.set_s(st.get_s()-2);

    cout<<"t3 = ";   t3.showTime();

   Time t_invalid(16,123,65);

    cout<<"Bad Time ";          

    t_invalid.showTime();

   system("pause");

   return 0;

}

    პროგრამის შესრულების შედეგია:

 String-Time 4:17:50

 t1 = 12:25:40

 t2 = 3:12:30

 After copy

 t1 = 3:12:30

 t3 = 3:22:48

 Bad Time 4:123:65

 Press any key to continue . . .

 

 

მივაქციოთ ყურადღება შეტყობინებას t1 =t2; იმისათვის, რომ ერთი ობიექტი მივანიჭოთ მეორეს,  გამოიყენება მინიჭების  =  ოპერატორი. ამ დროს სრულდება  ე.წ. ბიტური მინიჭება: ერთი ობიექტის ყოველი ელემენტის კოპირება მეორე ობიექტის შესაბამის ელემენტში (t2 ობიექტის თითოეული ბიტის კოპირება t1 ობიექტის შესაბამის ბიტში). უნდა გვახსოვდეს, რომ კოპირება უსაფრთხოა მხოლოდ ისეთი ობიექტებისათვის, რომლებიც არ მოითხოვენ დინამიკური მეხსიერების განაწილებას.

განხილულ Time კლასში არ სრულდება შეტანილი მონაცემების სისწორის შემოწმება. მონაცემების დასაშვები დიაპაზონიდან გასვლის შემოწმება უნდა ხდებოდეს ორივე კონსტრუქტორში და ყველა “set”-ტიპის ფუნქციაში.

ქვემოთ მოყვანილ პროგრამაში აგებულია და ტესტირებული Time კლასის გამარტივებული ვარიანტი

class Time{

   public:

      Time(int =12, int =25, int =40);

      void showTime();

   private:

        int hour;

      int minute;

      int second;

      void checkTime(int& , int& , int& );

};

რომელშიც მონაცემების კორექტულობა მოწმდება კონსტრუქტორში. ფუნქცია checkTime ამოწმებს რამდენად სწორია second, minute და hour მონაცემების სიდიდე და საჭიროების შემთხვევაში ასწორებს მათ. ფუნქციას იყენებს მხოლოდ კლასის კონსტრუქტორი, ამიტომაც  checkTime არის კლასის  private ფუნქცია.   

#include <iostream>
using namespace std;
class Time{
public:
Time(int =12, int =25, int =40);
void showTime();
private:
int hour;
int minute;
int second;
void checkTime(int &, int &, int &);

};

// კლასის რეალიზების ნაწილი

Time:: Time(int h, int m, int s)

{checkTime(h, m, s);    

   hour =h;   minute =m; second =s;

}

void Time:: showTime()

{

   cout<<((hour==0||hour==12)? 12:hour%12)

       <<":"<<(minute<10 ? "0" : "" )<<minute

       <<":"<<(second<10 ? "0" : "" )<<second<<endl;

}

void Time:: checkTime(int& h, int& m, int& s){

    int s1 =0, m1 =0;

    if(s < 0 ) s =-s;

    if(m < 0 ) m =-m;

    if(h < 0 ) h =-h;

    if(s > 60){

       s1 =s/60; s %=60;

       m +=s1;

    }

    if(m > 60) {

       m1 =m/60; m %=60;

       h +=m1;  

    }    

    if(h > 12) h %=12;      

}

int main()

{

   Time t1, t2(3,12,30);

    cout<<"t1 = ";  

          t1.showTime();

    cout<<"t2 = ";  

          t2.showTime();

    t1 =t2;  

    cout<<"After copy\nt1 = ";
t1.showTime();
Time t_invalid(-12,66,-61);
cout<<"Valid Time ";
t_invalid.showTime();
system("pause");
return 0;

}

პროგრამის შესრულების შედეგია: 

 t1 = 12:25:40

 t2 = 3:12:30

 After copy

 t1 = 3:12:30

 Valid Time 1:07:01

 Press any key to continue . . .

 

 

დესტრუქტორი. კონსტრუქტორის და დესტრუქტორის გამოძახების დრო და რიგი

     დესტრუქტორი  ეწდობა კლასის ფუნქციას, რომელიც ემსახურება კლასის ობიექტების “განადგურებას”. იგი ავტომატურად გამოიძახება მაშინ, როდესაც ობიექტი გადის მოქმედების არიდან: დესტრუქტორი მონიშნავს ასეთი ობიექტის მიერ დაკავებულ მეხსიერებას, როგორც თავისუფალს. დესტრუქტორის სახელი შედგება ~ სიმბოლოსა და კლასის სახელისგან. დესტრუქტორი არ აბრუნებს მნიშვნელობას და მას არა აქვს პარამეტრები. მაგალითად,  Time კლასის დესტრუქტორის პროტოტიპი იქნება    ~Time();

კლასში უნდა იყოს აღწერილი მხოლოდ ერთი დესტრუქტორი - დესტრუქტორების გადატვირთვა ნებადართული არ არის. როგორც წესი, დესტრუქტორების გამოძახება სრულდება კონსტრუქტორების გამოძახების შებრუნებული რიგით.

გლობალური ობიექტისათვის კონსტრუქტორი გამოიძახება პროგრამის შესრულების დასაწყისში, დესტრუქტორი კი - პროგრამის დასრულების შემდეგ.

ლოკალური auto ობიექტისათვის კონსტრუქტორი გამოიძახება მასზე განაცხადის მომენტში, ხოლო დესტრუქტორი - იმ ბლოკის დასრულების შემდეგ, სადაც ობიექტი აღწერილია (როდესაც ობიექტი გადის მოქმედების არიდან).

static ლოკალური ობიექტისათვის კონსტრუქტორი გამოიძახება მხოლოდ ერთხელ ამ ობიექტის ”გაჩენის” მომენტში. შესაბამისად, ასეთი ობიექტის დესტრუქტორი მუშაობს ერთხელ  - პროგრამის დამთავრების შემდეგ.     

განვიხილოთ საილუსტრაციო მაგალითი, რომელიც დაგვანახებს რა რიგით გამოიძახება სხვადასხვა ობიექტებისათვის კონსტრუქტორი და დესტრუქტორი:

#include <iostream>
using namespace std;
class Create_Destroy{

   public:
Create_Destroy(int, string); // კონსტრუქტორი
~Create_Destroy();// დესტრუქტორი
private:
int objectID; // ობიექტის ნომერი
string message; // ობიექტის აღწერა

};

// გლობალური ფუნქცია

void function(); 

// გლობალური ობიექტი

Create_Destroy first(1,"(global before main)");  

int main()

{

   cout << "\nMAIN: BEGINS" << endl;

   Create_Destroy second(2,"(local automatic in main)");  

   static Create_Destroy third(3,"(local static in main)");

   function();

   cout<<"\nMAIN: EXECUTION"<<endl;

   Create_Destroy fourth(4,"(local automatic in main)");

   cout<<"\nMAIN: ENDS"<< endl;

   system("pause");

   return 0;

}

// გლობალური ფუნქციის განსაზღვრა

void function()

  cout<<"\nFUNCTION: BEGINS"<<endl;

  Create_Destroy fifth(5,"(local automatic in function)");   

  static Create_Destroy sixth(6,"(local static in function)");

  cout<<"\nFUNCTION: ENDS"<< endl;

}

// კლასის რეალიზება

Create_Destroy::Create_Destroy(int ID, string mes){                                                                 

   objectID =ID;                    

   message =mes;                                                   

   cout<<"Object "<<objectID<<" constructor "     

       <<message<<endl;                                        

}

Create_Destroy::~Create_Destroy(){                                                          

   cout<<"Object "<<objectID<<" destructor "

       <<message<<endl;                                  

}

             პროგრამის შესრულების შედეგია:

 Object 1 constructor (global before main)

 MAIN: BEGINS

 Object 2 constructor (local automatic in main)

 Object 3 constructor (local static in main)

 FUNCTION: BEGINS

 Object 5 constructor (local automatic in function)

 Object 6 constructor (local static in function)

 FUNCTION: ENDS

 Object 5 destructor (local automatic in function)

 MAIN: EXECUTION

 Object 4 constructor (local automatic in main)

 MAIN: ENDS

 Object 4 destructor (local automatic in main)

 Object 2 destructor (local automatic in main)

 Object 6 destructor (local static in create)

 Object 3 destructor (local static in main)

 Object 1 destructor (global before main)

 Press any key to continue . . .

 

 

0Shares