ამასწინათ ინტერნეტში ვნახე ერთი სტატია სადაც ავტორი თავიდანვე ლაპარაკს იწყებდა იმაზე, რომ ვითომ ჩვენ რითი ვართ ნაკლები თუნდაც იმ Microsoft-ის პროგრამისტებზე, ან კიდევ იმათზე ვინც Counter Strike დაწერა, ან კიდევ STL-ის შემოქმედ ალექსი სტეპანოვზე, მოდით ავდგეთ და ჩვენც გავაკეთოთ რაიმე განსაკუთრებულიო. . .
მოკლედ ისეთი პრომოუშენი ჰქონდა, რომ გადაუკითხავად ამოვბეჭდე 45 გვერდიანი Шаг-ებით გავსებული სტატია.
მოდით მივიღოთ სერიოზული სახეები, ცოტა შუბლიც შევიკრათ, ხანდახან მაღლა ჭკვიანურადაც ავიხედოთ ხოლმე, მოვიდგათ გვერდზე ცივი ჩაი, ან კიდევ კოკაკოლა, ან კიდევ ყავა ან კიდევ სხვა რაიმე პახმელიაზე გამოსაყვანი და შევუდგეთ ამ სტატიის ერთ-ერთი ნაკვესის დამუღამებას.
მოკლედ, ვილაპარაკებთ კლასებსა და მაჩვენებლებზე C++-ში და მაშასადამე ამ სტატის მკითხველს გარკვეული წარმოდგენა უნდა ჰქონდეს კლასებზე, მის ველებზე, მეთოდებზე, მაჩვენებლებზე, პროგრამირების ზოგიერთ საბაზისო მცნებებზე და თქვენ წარმოიდგინეთ შაბლონებზეც, თუმცა აქ რთული არაფერი არ იქნება.
დავიწყოთ იქიდან, რომ ყოველი ობიექტი, რომელიც არის კლასის ტიპის, იკავებს გარკვეულ ადგილს მეხსიერებაში და თუ ამ კლასში მოვახდენთ მისი რომელიმე ველის განზომილების შეცვლას, შესაბამისადვე შეიცვლება ამ ობიექტის მიერ დაკავებული მეხსიერების ზომა.
მაინც, რაზეა ლაპარაკი? განვიხილოთ მაგალითი, ავიღოთ ასეთი მარტივი კლასი:
class CArray
{
private:
double x[3];
public:
void Set_x(int _i,double _x)
{
x[_i]=_x;
}
double Get_x(int _i)
{
return x[_i];
}
};
შევამოწმოთ თუ როგორ მუშაობს იგი:
void main()
{
CArray array;
array.Set_x(1,23.5);
cout << array.Get_x(1) << endl;
}
მივაკომპილოთ, გავუშვათ: პროგრამა დაწერს 23.5-ს; ყველაფერი რიგზეა.
ახლა შევამოწმოთ თუ რა ზომის მეხსიერებას იკავებს array ობიექტი. ამისათვის main-ში დავამატოთ ასეთი კოდი: cout << sizeof(array) << endl; გაშვების შემდეგ ბოლო სტრიქონი დაგვიწერს 24-ს, ანუ ჩვენს მიერ შემნილი ობიექტი იკავებს 24 ბაიტს. (აქვე უნდა აღვნიშნო, რომ იგივე შედეგს მივიღებთ თუ ჩვეულებრივ ობიექტს შევცვლით მაჩვენებლით და განვახორციელებთ შესაბამის სამისამართო არითმეტიკის ოპერაციებს.)
ახლა კლასის x ველის განზომილება გავზარდოთ ვთქვათ 10-მდე, ესე იგი ავიღოთ double x[10]; პროგრამა გაშვების შემდეგ დაგვიწერს 80-ს. ესე იგი array ობიექტი უკვე მოითხოვს დიდ მეხსიერებას.
მოდით ახლა ჩავიდინოთ ცოტათი კრიმინიზირებული საქციელი, მოვატყუოთ ეს ჩვენი C++ და შევქმნათ ასეთი აგებულების კლასი:
class CPThat
{
private:
CArray *aArray;
public:
CPThat(CArray *_that=NULL):aArray(_that){ ; }
~CPThat()
{
if(aArray) delete aArray;
}
operator CArray*()
{
return aArray;
}
CArray* operator->()
{
return aArray;
}
CPThat operator+(ptrdiff_t _offset)
{
return CPThat(aArray+_offset);
}
};
ეს არის ჩვეულებრივი კლასი, რომელსაც აქვს თავისი კონსტრუქტორი და დესტრუქტორი. ასევე მასში ხდება რამოდენიმე ოპერატორის, კერძოდ კი ->, + და *-ის გადატვირთვა. თუ საჭირო იქნება, შესაძლებელია უფრო მეტი ოპერატორი გადავტვირთოთ (-,++,--,/ და ა.შ.). ptrdiff_t განსაკუთრებული არაფერია, როგორც მახსოვს უბრალოდ int ტიპია, დაკავშირებული მაჩვენებლებთან. თუ არადა შეგიძლიათ ზუსტად ნახოთ MSDN-ში. ჩვენს შემთხვევაში ამას არა აქვს გადამწყვეტი მნიშვნელობა.
ახლა, რაში გვჭირდება ეს კლასი?
როგორც შევამჩნიეთ ამ კლასში ინკაფსულირებულია მაჩვენებელი CArray-ზე, ხოლო მეთოდებში კი ხდება რაღაც გარკვეული მანიპულაციები ამ მაჩვენებელზე. იმისათვის რომ ეს ყველაფერი უფრო ცხადი გავხადოთ, ფუნქცია main-ი შევცვალოთ ასე:
void main()
{
CPThat pArray(new CArray);
pArray->Set_x(1,23.5);
cout << pArray->Get_x(1) << endl;
}
მივაკომპილოთ და გავუშვათ: შეგეგია ისევ 23.5. ყველაფერი ისევ კარგად არის.
მაშ, აღვწერეთ ცვლადი pArray რომელიც არის ამ ჩვენი ახალი კლასის ტიპისა და მის კონსტრუქტორს გადავეცით მაჩვენებელი CArray ტიპზე, ხოლო შემდეგ როგორც ვხედავთ pArray-ს ვიყენებთ მასივის კლასის მეთოდების გამოსაძახებლად ისევე როგორც ადრე გამოვიყენეთ ჩვეულებრივი ობიექტი.
პრინციპში აქ ახალი არაფერია თუ კი არ ვიტყვი, რომ აი ამ ჩვენი უცნაური CPThat კლასის წყალობით რაც არ უნდა გავუზარდო x ველის განზომილება ფუნქცია sizeof ერთი და იგივე მნიშვნელობას დააბრუნებს. ჩვენთვის, ქართველებისთვის გასაგებ ენაზე რომ ვთქვათ ეს დაახლოები იგივეა, რომ 2 ჭიქიანი ყანწი 5 ჭიქიანით შეცვალო და წონა იგივე დარჩეს.
უი. . . . . !
ნუ, რა თქმა უნდა მთლად ზუსტი შედარება ვერ მოვიყვანე, მაგრამ ამ ახალწლების გადამკიდე სხვა არაფერი მომივიდა თავში.J.
აბა ვნახოთ რაზეა ლაპარაკი. მეინი შევცვალოთ ასე:
void main()
{
CPThat pArray(new CArray);
pArray->Set_x(1,23.5);
cout << pArray->Get_x(1) << endl;
cout << sizeof(pArray) << endl;
}
დამუშავების შემდეგ კოდის ბოლო სტრიქონი დაგვიწერს რიცხვს 4, ყოველ შემთხვევაში ჩემთან ასეა.
ახლა გავზარდოთ კლასის ველის განზომილება ანუ double x[10]; შევცვალოთ double x[100];-ით. მივაკომპილოთ, გავუშვათ და. . . .
თუ თქვენ ამ სტატიას რომანივით არ კითხულობთ და აქ მოყვანილ კოდს ამოწმებთ C++-ის რომელიმე კომპილატორით, მაშინ ჩემი აღნიშვნის გარეშეც ნახავდით რომ კოდი cout << sizeof(pArray) << endl; დაწერს იგივე რიცხვს - 4.
სიმართლე გითხრათ მთლად ბოლომდე ვერ გავიგე სად გაქრა ის 90*4 ბაიტი (თუ ჩავთვლით, რომ double იკავებს 4 ბაიტს), მაგრამ ფაქტია რომ პროგრამა ნორმალურად მუშაობს.
ისიც ფაქტია, რომ ეს მეხსიერება ჰაერში არ გამოეკიდებოდა და სადღაც ხდება მისი განთავსება.
ცხადია, საქმე გვაქვს მეხსიერებაში მონაცემთა დინამიკურ განლაგებასთან და როგორც ბატონმა MSDN-მა მიკარნახა ეს არის თვით sizeof ოპერატორის ეფექტი მაჩვენებლებთან დამოკიდებულებაშიო. აი რა წერია იქ: “To obtain the size of the pointer represented by the array identifier, pass it as a parameter to a function that uses sizeof. For example:. . . ”. ესე იგი ოპერატორი გვიბრუნებს მაჩვენებლის ზომას. პრინციპში ჩვენ ეს გვაწყობს.
ესე იგი ჩვენ ფაქტიურად მივიღეთ მაჩვენებლების კლასი, რომელიც შეიძლება გამოვიყენოთ სხვადასხვა დანიშნულებისამებრ. ეს უკვე ფანტაზიის საქმეა და იგი მკითხველისთვის მიმიდია.
როგორც ცნობილია დაახლოებით ამ სტილზეა გაკეთებული auto_ptr, STL-ში.
გამოცდილი პროგრამისტი დაინახავს რა კოდის ხეირიანობას, მაშინვე თავში მოუვა ის აზრი რომ გადააქციოს იგი შაბლონად და როგორც ძველ პროგრამისტებს ჩვევიათ კოდის ზომის შემცირების ხარჯზე გაიმარტივოს ცხოვრება.
მაშ, რაღა დაგვრჩენია ავდგეთ და ჩვენც გავაკეთოთ ამ ჩვენი საიდუმლოებით მოცული კლასის შაბლონი, რათა იგი ხელმისაწვდომი იყოს ყველა ტიპისათვის.
template <class T>
class SmartPointer
{
private:
T* tObj;
public:
SmartPointer(T* t=NULL):tObj(t);
~SmartPointer(){if(tObj)delete tObj;}
operator T*(){return tObj;}
T* operetor->(){return tObj;}
};
როგორც ზემოთ ავღნიშნეთ, აქ შეიძლება მოხდეს სხვა არითმეტიკული ან არაარითმეტიკული ოპერაციების გადატვირთვაც, ეს დამოკიდებულია იმაზე თუ რისთვის არის განკუთვნილი თქვენი კლასი – მასივია, უბრალო მაჩვენებელია თუ რა.
აი, ამჯერად სულ ეს იყო.
რეზიუმე
ეცადეთ კიდევ მოატყუოთ C++!
ავტორი: გიორგი ბაწაშვილი (G3B)
აღდგენილია სტატიის საწყისი ვარიანტი