Գլխավոր էջ » 2015 » Ապրիլ » 04 » C++–ն աջակցում է մեկնաբանություններ ինչպես C լեզվի ոճով՝
20:29
C++–ն աջակցում է մեկնաբանություններ ինչպես C լեզվի ոճով՝
C++–ն աջակցում է մեկնաբանություններ ինչպես C լեզվի ոճով՝

 /*
 Սա մեկնաբանություն է, որը կարող է կազմված լինել
 մի քանի տողերից
 */

այնպես էլ մեկ տողանի մեկնաբանություններ՝

 // այս տողի մնացած մասը համարվում է մեկնաբանություն

որտեղ //–ը նշանակում է մեկնաբանության սկիզբ, իսկ ամենամոտիկ տողանցման նշանը, որը \ նշանով (կամ նրան համարժեք ??/ նշանակմամբ) հայելիացված չէ, համարվում է մեկնաբանության ավարտ։ Ստացվում է, տվյալ տողի սահմաններում //–ից հետո գրված ամեն ինչ համարվում է մեկնաբանություն։
Տիպեր

C++ լեզվում հասանելի են հետևյալ ներկառուցված տիպերը՝

    Սիմվոլային՝ char, wchar_t (char16_t և char32_t, C++11 ստանդարտում)։
    Ամբողջաթվային նշանային՝ signed char, short int, int, long int (և long long int, C++11 ստանդարտում)։
    Ամբողջաթվային ոչ նշանային՝ unsigned char, unsigned short int, unsigned int, unsigned long int(և unsigned long long int, C++11 ստանդարտում)։
    Լողացող կետիկով՝ float, double, long double։
    Տրամաբանական՝ bool, որն կարող է ընդունել true և false արժեքներից մեկը։

Համեմատության գործողություններն վերադարձնում են bool տիպի արժեքներ։ if–ից և while–ից հետո դրվող փակագծերի արտահայտությունները բերվում են bool տիպի։[10]

Ֆունկցիաները կարող են ընդունել արգումենտներ հղումով։ Օրինակ, void f(int &x) {x=3;} ֆունկցիան վերագրում է իր արգումենտին 3 արժեքը։ Ֆունկցիաները կարող են նաև արդյունքները վերադարձնել հղումով, և այդ հղումներն կարող են ոչ մի կապ չունենալ ֆունկցիայի հետ։ Օրինակ, {double &b=a[3]; b=sin(b);} կոդը համարժեք է a[3]=sin(a[3]); կոդին։ Ծրագրավորման ժամանակ հղումներն ինչ–որ չափով նման են ցուցիչներին՝ հետևյալ առանձնահատկություններով․

    մինչև հղումների օգտագործումը դրանք պետք է նախօրոք ինիցիալիզացվեն,
    հղումը իր ողջ կյանքի ընթացքում ցույց է տալիս միևնույն հասցեն,
    արտահայտություններում հղումը նշանակում է այն օբյեկտը կամ այն ֆունկցիան, որին նա հղված է։ Այդ օբկյետին կամ ֆունկցիային հղման միջոցով դիմելիս անհրաժեշտ է ցուցչի հասցեի ստացում։

Գոյություն ունեն նաև այլ տարբերություններ ցուցչի և հղման օգտագործման միջև։ Ըստ գաղափարի, հղումը փոփոխականի կամ ֆունկցիայի մեկ այլ անունն է, միևնույն հասցեի մեկ այլ անվանումը։ Այն գոյություն ունի միայն ծրագրի կոդում և կոմպիլյացիայի ժամանակ փոխարինվում է իր հասցեով։ Իսկ ցուցիչը փոփոխական է, որում պահվում է հասցեն, որին դիմում են։
Զանազան

    inline սպեցիֆիկատորը թույլ է տալիս հայտարարել inline–ֆունկցիաներ։ Ֆունկցիան, որը որոշված է դասի մարմնի ներսում, լռելյան inline է համարվում։ Ի սկզբանե inline–ֆունկցիաները մտածված էին այնպես, որ նրանք բարելավման համար լավ թեկնածուներ էին, քանի որ այն տեղերում, որտեղ կանչվում էր ֆունկցիան, կոմպիլյատորը պետք է ուղղակի տեղադրեր ֆունկցիայի մարմինը, այլ ոչ թե առանձին կատարեր ֆունկցիան և տեղադրեր արժեքը։ Իրականում կոմպիլյատորը պարտադիր չէ, որ իրականացնի inline–ֆունկցիայի մարմնի տեղադրումը այնտեղ, որտեղից կանչվում է այդ ֆունկցիան, բայց նա կարող է, ելնելով բարելավման տրված աստիճանից, այդպես վարվել ոչ inline–ֆունկցիաների հետ։ inline-ֆունկցիաների ամենամեծ առանձնահատկությունն այն է, որ այն կարող է բազմակի անգամ հայտարարվել մի քանի թարգմանման միավորներում (միևնույն ժամանակ inline-ֆունկցիան պետք է հայտարարված լինի բոլոր այն թարգմանման միավորներում, որտեղ այն օգտագործվում է), իսկ inline չհանդիսացող ֆունկցիաները կարող են հայտարարվել ծրագրում մեկից ավել անգամ։ Օրինակ՝

inline double Sqr(double x) {return x*x;}։

    volatile նկարագրիչը օգտագործվում է փոփոխականների նկարագրման ժամանակ և տեղեկացնում է կոմպիլյատորին, որ տվյալ փոփոխականի արժեքը կարող է փոխվել կոմպիլյատորի կողմից անհայտ եղանակով։ Այն փոփոխականների համար, որոնք հայտարարված են որպես volatile, կոմպիլյատորը չպետք է կիրառի այնպիսի բարելավման միջոցներ, որոնք փոփոխում է փոփոխականի տեղը հիշողության մեջ (օրինակ, այն ռեգիստր տեղափոխող միջոցներ) կամ ենթադրում են, որ փոփոխականի արժեքը նրա երկու վերագրման գործողությունների միջև չի փոփոխվելու։ Բազմամիջուկ համակարգերում volatile–ն օգնում է խուսափել հիշողության 2–րդ կարգի խոչնդոտներից։
    Եթե նկարագրված է կառուցվածք, դաս, միավորում (union) կամ թվարկում (enum), նրա անունը հանդիսանում է տիպի անուն, օրինակ՝

struct Time {
    int hh, mm, ss;
};
Time t1, t2;

    Ավելացված են անվանատարածքները (namespace)։ Օրինակ, եթե գրենք՝

namespace Foo
{
   const int x=5;
   typedef int** T;
   void f(int y) {return y*x};
   double g(T);
   ...
}

ապա ձևավոր փակագծերից դուրս անհրաժեշտ է T–ին, x–ին, f–ին, g–ին դիմելիս պետք է համապատասխանաբար գրել այսպես՝ Foo։։T, Foo։։x, Foo։։f և Foo։։g։ Եթե որևէ ֆայլում անհրաժեշտ է անմիջականորեն դիմել այդ անվանատարածքի անդամներին, կարելի է գրել՝

using namespace Foo;

Կամ այսպես՝

using Foo::T;

Անվանատարածքներն անհրաժեշտ են, որպեսզի համընկնող անուններով գլոբալ փոփոխականներ, ֆունկցիաներ և տիպեր ունեցող փաթեթների միջև անհամաձայնություններ չառաջանան։ Որպես մասնավոր դեպք կարելի է նշել անանուն անվանատարածքը՝

namespace
{
    ...
}

Նրանում հայտարարված բոլոր անունները հասանելի են միայն ընթացիկ թարգմանման միավորում և ուրիշ ոչ մի տեղ։

    Ֆունկցիայի մեկ կամ մի քանի վերջին արգումենտները կարող են տրվել լռելյան։ Օրինակ, եթե ֆունկցիան նկարագրված է որպես void f(int x, int y=5, int z=10), ֆունկցիայի f(1), f(1,5) և f(1,5,10) կանչերը համարժեք են միմյանց։
    Ֆունկցիաների հայտարարման ժամանակ փակագծերում արգումենտների բացակայությունը նշանակում է, որ արգումենտներ չկան, այլ ոչ թե, որ դրանք անհայտ են, ինչպես C լեզվում է։ Եթե արգումենտներն անհայտ են, ապա պետք է օգտվել բազմակետերից, օրինակ, int printf(const char* fmt, ...)։
    Կառուցվածքի կամ դասի մեջ կարելի է նկարագրել ներդրված տիպեր, ինչպես typedef–ի միջոցով, այնպես էլ այլ դասերի, ինչպես նաև՝ թվարկումների նկարագրմամբ։ Դասից դուրս այդպիսի տիպերին դիմելու համար նրանց անվանը ավելացվում է կառուցվածքի կամ դասի անունը և երկու հատ երկու կետ՝

struct S
{
    typedef int** T;
    T x;
};
S::T y;

    Կարող են լինել միևնույն անվանումով, բայց տարբեր տիպերով կամ արգումենտների քանակությամբ ֆունկցիաներ։ (ֆունկցիաների վերբեռնում․ վերադարձվող արժեքների տիպը չի ազդու վերբեռնման վրա)։ Օրինակ, կարող ենք գրել՝

void Print(int x);
void Print(double x);
void Print(int x, int y);

    Օգտագործողի տիպերի վրա կիրառվող որոշ օպերատորների իմաստը կարելի է որոշել համապատասխան օպերատորային ֆունկցիաների հայտարարման միջոցով։ Օրինակ, այսպես՝

struct Date {int day, month, year;};
void operator ++(struct Date& date);

Օպերատորային ֆունկցիաները շատ բաներում նման են սովորական (ոչ օպերատորային) ֆունկցիաներին։ Բացառությամբ new, new[], delete և delete[] օպերատորների, չի կարելի վերասահմանել ներկառուցված տիպերի պահվածքը (ասենք, վերասահմանել int տիպի արժեքների բազմապատկման օպերատորը)։ Չի կարելի ստեղծել նոր օպերատորներ, որոնք չկան C++–ում (ասենք, **)։ Չի կարելի փոխել օպերատորների համար նախատեսված օպերանդների քանակը, ինչպես նաև չի կարելի փոխել օպերատորների գոյություն ունեցող առաջնահերթություններն ու ասոցիատիվությունը (ասենք, a+b*c արտահայտության մեջ նախ պետք է կատարվի բազմապատկման գործողությունը, և հետո միայն գումարման, ինչ տիպի էլ որ լինեն a–ն, b–ն և c–ն)։ Կարելի է վերասահմանել [] (մեկ պարապետրով) և () (ցանկացած քանակությամբ պարամետրերով) գործողություները։

    Ավելացվել են կաղապարները (template)։ Օրինակ, template<class T> T Min(T x, T y) {return x<y?x:y;} կոդը հայտարարում է Min ֆունկցիան ցանկացած տիպի համար։ Կաղապարները կարող են տալ ոչ միայն ֆունկցիաներ, այլ նաև տիպեր։ Օրինակ, template<class T> struct Array{int len; T* val;}; կոդը հայտարարում է ցանկացած տիպի արժեքների զանգված, որից հետո մենք կարող ենք գրել Array<float> x;
    Ի հավելումբ malloc և free ֆունկցիաների, ներմուծված են նաև operator new, operator new[], operator delete և operator delete[] օպերատորային ֆունկցիաները, ինչպես նաև new, new[], delete և delete[] օպերատորները։ Եթե T–ն ցանկացած օբյեկտային տիպ է և չի հանդիսանում զանգվածի տիպ, tt>X–ը՝ ցանկացած օբյեկտային տիպ և A–ն՝ զանգվածի տիպ, որն ունի որոշակի n քանակությամբ տարր, որոնք ունեն X տիպ, ապա
        new T–ն հիշողություն է հատկացնում (operator new ֆունկցիան կանչելու միջոցով), որն բավարար է Т տիպի մեկ արժեք տեղավորելու համար, հնարավոր է, այդ հիշողության մեջ ինիցիալիզացնում է օբյեկտը և վերադարձնում է Т* տիպի ցուցիչ (օրինակ, Т* p = new T)։
        new X[n]–ն ու new A–ն հիշողություն են հատկացնում (operator new[] ֆունկցիան կանչելու միջոցով), որն բավարար է X տիպի n հատ օբյեկտ տեղավորելու համար, հնարավոր է, ինիցիալիզացնում է յուրաքանչյուր օբյեկտը այդ հիշողության մեջ, և վերադարձնում է X* տիպի ցուցիչ (օրինակ, X* p = new X[n])։
        delete p, հեռացնում է օբյեկտը (որը չի հանդիսանում զանգված), որին հղվում է p ցուցիչը, և ազատում է հիշողության այն հատվածը (operator delete ֆունկցիայի կանչման միջոցով), որն հատկացված է նրա համար new արտահայտության կողմից։
        delete [] p, հեռացնում է զանվածի բոլոր օբյեկտները, որին հղվում է p ցուցիչը, և ազատում է հիշողության այն հատվածը (operator delete[] ֆունկցիայի կանչման միջոցով), որն հատկացված է այդ զանգվածին new արտահայտության կողմից։

delete գործողությունը ստուգում է, արդյո՞ք իր արգումենտը զրոյական ցուցիչ չէ, հակառակ դեպքում նա ոչինչ չի անում։ non-POD դասային տիպի օբյեկտ ինիցիալիզացնելու համար new արտահայտություւնը կանչում է կառուցողին։ Դասային տիպի օբյեկտի հեռացման համար delete արտահայտությունը կանչում է ապակառուցողին (տես՝ ներքևում)։
Օբյեկտային կողմնորոշված հնարավորությունները

C++-ը, ի տարբերություն C լեզվի, ունի օբյեկտային կողմնորոշված հնարավորություններ։ Այն ունի դասեր, որոնք ապահովում են օբյեկտային կողմնորոշված ծրագրավորման երեք կարևոր հատկությունները՝ ինկապսուլյացիան, ժառանգությունը և պոլիմորֆիզմը։

C++-ի ստանդարտում դաս (class) ասելով հասկանում են օգտագործողի կողմից ստեղծված տիպ, որն հայտարարված է class, struct կամ union բանալի բառերից մեկի օգնությամբ, կառույց (structure) ասելով հասկանում ենք դաս, որն հայտարարված է struct բանալի բառի օգնությամբ, և միացություն (union) ասելով հասկանում են դաս, որն հայտարարված է union բանալի բառի օգնությամբ։
Ֆունկցիաների նկարագրումը դասի մարմնում

Դասի մարմնում կարող ենք գրել միայն ֆունկցիայի անունը, կամ նկարագրել այն ամբողջությամբ (տես ներքևում տեղադրված Alloc ֆունկցիայի օրինակը։ Այս դեպքում այն համարվում է ներկառուցվող (inline))։
Հաստատուն ֆունկցիա–անդամներ

Ոչ ստատիկ ֆունկցիա-անդամները (և միայն նրանք) կարող են ունենալ const նկարագրիչը՝

class Array
{
...
    inline double operator[] (int n) const;

Այսպիսի ֆունկցիաները չեն կարող փոփոխել դասի դաշտերը (բացի այն դաշտերից, որոնք որոշված են որպես mutable)։ Եթե նրանք փորձեն դա անել, կոմպիլյատորը կարտարծի սխալի մասն հաղորդագրություն։
Ժառանգում

C++-ում, երբ մի դաս ժառանգվում է մեկ այլ դասի կողմից, ապա վերջինս ժառանգում է իր ծնող-դասի իրականացումը։ Ժառանգորդ-դասը կարող է ավելացնել իր դաշտերն ու ֆունկցիաները, կամ փոխարինել հիմնական դասի ֆունկցիաները։ C++-ում թույլատրվում է բազմակի ժառանգումը։

Ժառանգորդի կառուցողը կանչում է հիմնական դասերի կառուցողներին, այնուհետև ոչ ստատիկ անդամ-տվյալների կառուցողներին, որոնք հանդիսանում են դասի օրինակներ։ Ապակառուցողը նույնը կատարում է հակառակ հերթականությամբ։

Ժառանգումը լինում է հրապարակային, պաշտպանված և փակ (այսինքն փակ տիպի)՝
Հիմնական դասի անդամի տեսակը/ժառանգման ռեժիմը     private-անդամ     protected-անդամ     public-անդամ
private-ժառանգում     անհասանելի է     private     private
protected-ժառանգում     անհասանելի է     protected     protected
public-ժառանգում     անհասանելի է     protected     public

Կարելի է ասել, որ ժառանգորդը հիմնական դասի ընդլայնված տարբերակն է, այսինքն, եթե հիմնական դասի ժառանգումը բաց է, այն կարող ենք օգտագործել այնտեղ, որտեղ օգտագործվում է հիմնական դասը։ Հակառակը անել հնարավոր չէ։
Պոլիմորֆիզմ

C++-ի տիպերի համակարգի քերականությունը պոլիմորֆ չէ (ի տարբերություն ML-ի ժառանգորդների, այդ թվում C լեզվի հիբրիդների՝ BitC, Cyclone), սակայն կան պոլիմորֆ վարքն ապահովելու մի քանի եղանակներ։ Ամենից առաջ դա ժառանգման ժամանակ դասերի մեթոդների վերբեռնումն է՝ տվյալների աբստրակցիայի ապահովման օբյեկտային կողմնորոշված ծրագրավորման համար ավանդական դարձած եղականը։ Այնուհետև պարամետրիկ պոլիմորֆիզմի (C++-ի համայնքում սովորաբար անվանվող «ընհանրացված ծրագրավորման») համար կա իրականացման երկու եղանակ։ Առաջին եղանակը ժառանգված է C-ից։ Այն հիմնված է առանց տիպի ցուցչի օգտագործման և տիպի այլ տվյալներից կախված բերման վրա։ Սա C++-ում ավանդաբար համարվում է վտանգավոր եղանակ։ Երկրորդ եղանակը կաղապարների օգտագործումն է, սակայն, ի տարբերություն պարամետրիկ պոլիմորֆիզմի սովորական իրականացումների, C++-ում տեղի է ունենում վերբեռնված մոնոմորֆ ֆունկցիաների ընտանիքի ինքնուրույն գեներացիա, որն հիմնված է պոլիմորֆ որոշչի (կաղապարի)՝ համապատասխանաբար նրա օգտագործման կոնտեքսի վրա, այսինքն ելատեքստային կոդի մակարդակի պարամետրիկ պոլիմորֆիզմը թարգմանվում է մեքենայական մակարդակի իրավիճակայինի (ad hoc), ինչի համար C++-ն ենթարդկվում է քննադատությունների։ C++-ում կա նաև վերբեռնման երրորդ եղանակ՝ օպերատորների վերբեռնումը, որը դասերի ժառանգման հետ համադրված տալիս է էլ ավելի շատ հնարավորություններ կոդի ընթերցումն հեշտացնելու համար ի շնորհիվ, այսպես կոչված, «շարահյուսական շաքարի» ներմուծման։

Տվյալների աբստրակցիան ապահովելու համար անհրաժեշտ է կապել մի քանի դասեր ժառանգման հիերարխիայի մեջ և ֆունկցիաները նշանակել միանման սպեցիֆիկատորներով։ Օրինակ՝

class Figure
{
    ...
    void Draw() const;
    ...
};
 
class Square : public Figure
{
    ...
    void Draw() const;
    ...
};
 
class Circle : public Figure
{
    ...
    void Draw() const;
    ...
};
 
class Window
{
    ...
    void Draw() const;
    ...
};
 
class SquareWindow : public Window
{
    ...
    void Draw() const;
    ...
};
 
class RoundWindow : public Window
{
    ...
    void Draw() const;
    ...
};

Այս սահմանումների կոմպիլյացման արդյունքում ձևավորվում են վեց ֆունկցիաների մարմիններ։ Կոդում դրանք օգտագործվում են միանման․ ֆունկցիայի որոշակի օրինակի ընտրությունը կատարվում է կախված այն օբյեկտի օրինակի տիպից, որի համար կանչվում է ֆունկցիան։ Ֆունկցիաների վարքի համաձայնեցվածությունը պետք է ապահովի ծրագրավորողը։

Circle *c = new Circle(0,0,5);
Figure *f = c; // ճիշտ է․ Figure-ը Circle-ի հիմնական դասն է
c->Draw();
f->Draw(); // Ցուցիչները իրար հավասար են, սակայն f-ի և c-ի համար կկանչվեն տարբեր ֆունկցիաներ
SquareWindow *sw = new SquareWindow(0,0,5);
sw->Draw(); // օգտագործվում է նույնկերպ
f = sw; // սխալ է․ SquareWindow-ն չի մտնում Figure-ի ժառանգորդների ցանկում

Ինչպես երևում է, C++-ում պոլիմորֆիզմի այս տեսակի շրջանակը սահմանափակվում է ցուցակով տրված տիպերի նախագծման փուլում։ Այսպիսի պոլիմորֆիզմը լռելյան համարվում է ստատիկ, սակայն virtual սպեցիֆիկատորի օգտագործման ժամանակ այն վերածվում է դինամիկի՝

class Figure
{
    ...
    virtual void Draw() const;
    ...
};
 
class Square : public Figure
{
    ...
    void Draw() const;
    ...
};
 
class Circle : public Figure
{
    ...
    void Draw() const;
    ...
};
 
Figure* figures[10];
figures[0] = new Square(1, 2, 10);
figures[1] = new Circle(3, 5, 8);
...
for (int i = 0; i < 10; i++)
    figures[i]->Draw();

Այս դեպքում զանգվածի յուրաքանչյուր տարրի համար կկանչվի Square::Draw()-ն կամ Circle::Draw()-ն, կախված մաևմնի տեսակից։

Մաքուր վիրտուլ ֆունկցիա են անվանում այն վիրտուալ ֆունկցիա-անդամը, որն մարմնի փոխարեն հայտարարված է = 0 սպեցիֆիկատորով՝

class Figure
{
    ...
    virtual void Draw() const = 0;
);

Մաքուր վիրտուալ ֆունկցիան սահմանում չունի և չի կարող ուղղակիորեն կանչվել։ Այսպիսի ֆունկցիայի հայտարարման նպատակն է ստեղծել ընհանուր հիմնական դասում սիգնատուր-նախատիպ, որն չունի սեփական սահմանում, բայց թույլ է տալիս ստեղծել այդպիսի սահմանումներ ժառանգորդ-դասերում և ցուցիչի միջոցով կանչել դրանք ընդհանուր հիմնական դասին։ Ֆունկցիա-անդամը հայտարարվում է մաքուր վիրտուալ այն ժամանակ, երբ նրա սահմանումը հիմնական դասի համար իմաստ չունի։ Այսպես, վերընշված օրինակում Figure հիմնական դասի համար Draw() ֆունկցիայի սահմանումը իմաստ չունի, քանի որ «մարմիններ» ընդհանրապես չեն ստացվում և դրանք հնարավոր չէ ցուցադրել, սակայն այսպիսի ֆունկցիայի սահմանումը անհրաժեշտ է, որպեսզի կարելի լինի ժառանգորդ-դասերում նրան վերասահմանել և այդ դասերի Draw մեթոդը կանչել Figure-ի ցուցչի միջոցով։ Հետևաբար, միանգամայն տրամաբանական է հայտարարել Figure.Draw()-ն որպես մաքուր վիրտուալ ֆունկցիա-անդամ։

C++-ում մաքուր վիրոտուալ ֆունկցիայի հասկացության հետ սերտորեն կապված է «աբստրակտ դաս» հասկացությունը։ Աբստրակտ դաս են անվանում այն դասը, որն ունի գոնե մեկ չվերասահմանված մաքուր վիրտուալ ֆունկցիա-անդամ։ Այսպիսի դասերի օրինակների ստեղծումը թույլատրված չէ։ Աբստրակտ դասերը կարող են օգտագործվել միայն ժառանգման ճանապարհով նոր դասերի ստեղծման համար։ Եթե աբստրակտ դասի ժառանգորդ-դասում ժառանգված բոլոր մաքուր վիրտուալ ֆունկցիաները վերասահմանված չեն, ապա այդպիսի դասը նույնպես հանդիսանում է աբստրակտ դաս և նրա վրա նույնպես տարածվում են նշված բոլոր սահմանափակումները։

Աբստրակտ դասերը հաճախ օգտագործվում են որպես միջերեսներ։ Ի տարբերություն այլ լեզուների միջերեսների, C++ լեզվի աբստրակտ դասերը կարող են ունենալ ոչ վիրտուալ ֆունկցիաներ և անդամ-տվյալներ։
Ինկապսուլյացիա

C++-ում տեղեկատվության կազմակերպման հիմնական միջոց են համարվում դասերը։ Ի տարբերություն C լեզվի կառույցների (struct), որոնք կարող են կազմված լինել միայն դաշտերից և դերդրվող տիպերից, C++-ի դասը (class) կարող է կազմված լինել դաշտերից, ներդրված տիպերից և ֆունկցիա-անդամներից (member functions)։ C++-ում ինկապսուլյացիան իրականացվում է դասի անդամների հասանելիության մակարդակի նշման միջոցով։ Դրանք լինում են հրապարակային (բաց, public), պաշտպանված (protected) և սեփական (փակ, գաղտնի, private)։ C++-ում կառույցները դասերից տարբերվում են միայն նրանով, որ դրանցում անդամները և հիմնական դասերը հրապարակային են, իսկ դասերում՝ սեփական։
Հասանելիություն     private     protected     public
Հենց դասը     այո     այո     այո
Ընկերներ     այո     այո     այո
Ժառանգորդներ     ոչ     այո     այո
Դասից դուրս     ոչ     ոչ     այո

Հասանելիության ստուգումը կատարվում է կոմպիլյացիայի ժամանակ, դասի ոչ հասանելի անդամին դիմելու փորձը կհանգեցնի կոմպիլյացիայի սխալի։

Դասի օրինակ, որն իրականացնում է միաչափ զանգված՝

class Array
{
    public:
        Array() :
            len(0),
            val(NULL)
        {}
 
        Array(int _len) :
            len(_len)
        {
            val = new double[_len];
        }
 
        Array(const Array & a);
 
        ~Array()
        {
            Free();
        }
 
        inline const double & Elem(int i) const
        {
            return val[i];
        }
 
        inline void ChangeElem(int i, double x)
        {
            val[i] = x;
        }
 
    protected:
        void Alloc(int _len)
        {
            if (len == 0)
                Free();
 
            len = _len;
            val = new double[len];
        }
 
        void Free()
        {
            delete [] val;
            len = 0;
        }
 
        int len;
        double * val;
};

Այստեղ Array դասն ունի 2 հրապարակային ֆունկցիա-անդամներ, 2 պաշտպանված դաշտեր, 3 հրապարակային կառուցողներ և հրապարակային ապակառուցող։ inline նկարագրիչը հուշում է կոմպիլյատորին, որ ֆունկցիան կանչելու փոխարեն անրաժեշտ է այն ներկառուցել կանչելու վայրում, ինչի շնորհիվ հաճախ կարելի է հասնել մեծ արդյունավետության։
Ընկերներ

Ֆունկցիա-ընկերները այն ֆունկցիաներն են, որոնք չեն հանդիսանում ֆունկցիա-անդամներ, բայց, չնայած դրան, դասի պաշտպանված և փակ անդամները հասանելի են նրա համար։ Այս ֆունկցիաները դասի մարմնում պետք է հայտարարված լինեն friend բանալի բառով։ Օրինակ՝

class Matrix {
    ...
    friend Matrix Multiply(Matrix m1, Matrix m2);
    ...
};
 
Matrix Multiply(Matrix m1, Matrix m2) {
    ...
}

Այստեղ Multiply ֆունկցիան կարող է դիմել Matrix դասի ցանկացած դաշտի կամ ֆունկցիա-անդամի։

Որպես ընկեր կարող է հայտարարվել ինչպես ամբողջ դասը, այնպես էլ դասի ֆունկցիա-անդամը։ Եթե A դասը հայտարարված է B դասում որպես ընկեր, ապա A դասի բոլոր սեփական (չժառանգված) ֆունկցիա-անդամները կարող են դիմել B դասի ցանկացած անդամի։ Օրինակ՝

class Matrix {
    ...
    friend class Vector::getNum( Matrix & ) ;
    ...
private:
    int i;
};
...
class Vector
{
    int GetNum( Matrix & m ){ return m.i;} // դիմում Matrix դասի տվյալների փակ անդամին
};

Դիմման օրինակ՝

void SomeFunction()
{
    Matrix m;
    Vector v;
 
    int i = Vector::GetNum( m );
}

Չորս կարևոր սահմանափակումներ, որոնք գործում են C++-ում ընկերական «հարաբերությունների» բրա՝

    Ընկերությունը տարանցիկ չէ։ Եթե A-ն B-ին հայտարարում է որպես ընկեր, իսկ B-ն, իր հերթին, ընկեր է հայտարարում C-ին, ապա C-ն ինքնըստինքյան A-ի ընկերը չի դառնում։ Դա անելու համար A-ն պետք է հայտարարի C-ին որպես իր ընկեր։
    Ընկերությունը փոխադարձ չէ։ Եթե A-ն հայտարարում է B-ին որպես ընկեր, ապա վերջինս ինքնըստինքյան չի դառնում A-ի ընկերը։ Դա անելու համար B դասում պետք է գոյություն ունենա A դասի հստակ ընկերության հայտարարում։
    Ընկերությունը չի ժառանգվում։ Եթե A դասը հայտարարում է B դասին որպես իր ընկեր, ապա B-ի ժառանգորդները ինքնըստինքյան չեն դառնում A դասի ընկերներ։ Դա անելու համար դրանցից յուրաքանչյուրը պետք է հստակ ձևով հայտարարի A-ին որպես ընկեր։
    Ընկերությունը չի տարածվում ժառանգորդների վրա։ Եթե A դասը հայտարարում է B-ին որպես ընկեր, ապա B-ն ինքնըստինքյան չի դառնում A-ի ժառանգորդ դասերի ընկերը։ Յուրաքանչյուր ժառանգորդ, անհրաժեշտության դեպքում, ինքնուրույն պետք է հայտարարի B-ին որպես իր ընկեր։

Ընդհանուր առմամբ, այս կանոնը կարելի է ձևակերպել այսպես․ «Ընկերական հարաբերություն գոյություն ունի միայն այն դասերի (դասի կամ ֆունկցիայի) միջև, որոնց համար այն հստակ հայտարարված է կոդում և գործում է միայն այն ուղղությամբ, որով այն հայտարարված է»։

Ըստ C++-ի գործող ստանդարտի, ներդրված դասը շրջապատող դասի փակ անդամների հասանելիության իրավունք չունի և չի կարող հայտարարել նրան որպես իր ընկեր (վերջինս հետևում է ընկեր եզրի՝ որպես դասի ոչ անդամ սահմանումից)։ C++0x ապագա ստանդարտում այս սահմանափակումները կվերացվեն։ Այս հարցում VC++, GNU C++ և Comeau C++ կոմպիլյատորների վերջին տարբերակները նույնիսկ անջատված ընդլայնումներով հետևում են նոր կանոններին, որոնք ձևակերպված են C++0x-ի վերջին սևագրություններում միայն։
Կոնստրուկտորներ և դեստրուկտորներ

Դասերն միշտ ունեն հատուկ ֆունկցիաներ՝ Կոնստրուկտոր և դեստրուկտորներ, որոնք կարող են հայտարարվել հստակ կամ ոչ հստակ։

Կոնստրուկտորը կանչվում է օբյեկտի (համապատասխան տիպի) ստեղծման ժամանակ՝ այն ինիցիալիզացնելու նպատակով, իսկ Դեստրուկտորը՝ օբյեկտի վերացման համար։ Մասնավորապես, Կոնստրուկտորը կարող է կանչվել՝ դասային տիպի փոխակերպումը կատարելու համար։

Կոնստրուկտորները նշանակվում են որպես դասի անունը կրող ֆունկցիաներ (օրինակ, Array::Array)։ Դեստրուկտորը նույնպես կրում է դասի անունը, սակայն անունից առաջ դրվում է ~ նշանը (օրինակ, Array::~Array)։ Կոնստրուկտորների և Դեստրուկտորների համար չի կարելի նշել վերադարձվող արժեքի տիպը։ Դեստրուկտորը չի կարող ընդունել արգումենտներ։ Դասը կարող է ունենալ մի քանի Կոնստրուկտորներ (տարբեր պարամետրերով), այդ թվում՝ կաղապարային, սակայն միայն մեկ՝ ոչ կաղապարային Դեստրուկտոր։

Այն Կոնստրուկտորը, որը չունի պարամետրեր կամ որի պարամետրերը ունեն լռելյան արգումենտներ, անվանում են լռելյան Կոնստրուկտոր։ Եթե ոչ կաղապարային Կոնստրուկտորը, որի առաջին պարամետրը հղում է իր դասին (օրինակ, Array::Array(const Array&)), իսկ մնացած պարամետրերը (եթե այդպիսիք կան)՝ ոչ, և որը ունի լռելյան արգումենտներ, անվանում են պատճենման Կոնստրուկտոր։ Այն կանչվում է գոյություն ունեցող օբյեկտի պատճեն հանիսացող նոր օբյեկտի ստեղծման ժամանակ՝

Array a(5); // կանչվում է Array::Array(int)
Array b;    // կանչվում է Array::Array()
Array c(a); // կանչվում է Array::Array(const Array&)
Array d=a;  // կանչվում է Array::Array(const Array&)
b=c;        // կանչվում է = օպերատորը
            // եթե այն որոշված չէ (ինչպես այս դեպքում), ապա կանչվում է կոմպիլյատորի կողմից  գեներացված վերագրման օպերատորը, որը
            // իրականացնում է հիմնական ենթաօբյեկտների պատճենում և ոչ ստատիկ անդամ-տվյալների տերնար պատճենում։
            // որպես կանոն, պատճենի Կոնստրուկտորը և վերագրման օպերատորը վերաիմաստավորվում են զույգերով

Եթե դասում չկա Կոնստրուկտորների հստակ հայտարարում, դասն ունի առանց պարամետրերի ոչ հաստակ հայտարարված Կոնստրուկտոր, որն կառուցում է ծնող-դասերի ենթաօբյեկտները և ինիցիալիզացնում է դասի դաշտերը՝ կանչելով դրանց լռելյան Կոնստրուկտորները։ Եթե դասում չկա հստակ հայտարարված պատճենող Կոնստրուկտոր, ապա դասն ունի ոչ հստակ հայտարարված պատճենող Կոնստրուկտոր, որն կատարում է աղբյուր հանդիսացող օբյեկտի բոլոր հայտարարված դաշտերի ուղիղ պատճենում համապատասխանաբար ընդունող օբյեկտի դաշտեր համապատասխան պատճենող Կոնստրուկտորների օգնությամբ։ Եթե դասում չկա հստակ հայտարարված Դեստրուկտոր, ապա դասըն ունի սեփական ոչ հստակ հայտարարված Դեստրուկտորը։

C++-ում Կոնստրուկտորները չեն կարող հայտարարվել վիրտուալ, իսկ Դեստրուկտորները՝ կարող են, և սովորաբար այդպես էլ հայտարարվում են, որպեսզի ապահովվի հղումով կամ ցուցիչով հասանելի օբյեկտի ճիշտ ոչնչացումը՝ անկախ հղման կամ ցուցչի տիպից։
Օպերատորների գերբեռնում

Ֆունկցիա-անդամները կարող են օպերատորներ լինել՝

class Array {
...
    inline double& operator[] (int n) { return val[n]; }

Այնուհետև

Array a(10);
...
double b = a[5];

Ստանդարտ գրադարանը
Ընդհանուր կառուցվածքը

C++-ի ստանդարտ գրադարանը իր մեջ ներառում է որոշակի միջոցների հավաքածու, որոնք պետք է հասանելի լինեն լեզվի ցանկացած իրականացման մեջ, որպեսզի ծրագրավորողներին ապահովեն լեզվի ընձեռած հնարավորությունների հարմարավետ օգտագործումը և ստեղծեն այն հիմքը, որն անհրաժեշտ է ինչպես ամենալայն սպեկտրի կիրառական հավելվածների, այնպես էլ մասնագիտացված գրադարանների մշակման համար։ С++-ի ստանդարտ գրադարանը իր մեջ ներառում է C լեզվի ստանդարտ գրադարանի մի մասը։ C++-ի ստանդարտը նորմատիվ հղում է պարունակում C-ի 1990 թվականի ստանդարտին և ինքնուրույն չի սահմանում ստանդարտ գրադարանի այն ֆունկցիաները, որոնք վերցված են C-ի ստանդարտ գրադարանից։

C++ լեզվի ստանդարտ գրադարանի հնարավորությունների հասանելիությունը ապահովվում է ծրագրում համապատասխան ստանդարտ գլխագիր ֆայլերի միացմամբ (#include դիրեկտիվի օգնությամբ)։ C++11 ստանդարտում սահմանված ընդամենը 79 այդպիսի ֆայլ։ Ստանդարտ գրադարանի միջոցները հայտարարված են որպես std անվանատարածք մտնող։ Այն գլխագիր ֆայլերը, որոնց անունները համապատասխանում են «cX» կաղապարին, որտեղ X-ը՝ C լեզվի ստանդարտ գրադարանի գլխագիր ֆայլի անվանումն է՝ առանց ընդլայնման (cstdlib, cstring, cstdio և այլն), պարունակում են սահմանումներ, որոնք համապատասխանում են C լեզվի ստանդարտ գրադարանի տվյալ հատվածին։ Միևնույն ժամանակ ծրագրավորողը կարող է օգտվել այնպիսի գլխագիր ֆայլերից, որոնց անվանումը համապատասխանում է «cX.h» կաղապարին (ավելացված է C-ի գլխագիր ֆայլերի «.h» ստանդարտ ընդլայնումը), որոնց մեջ նույն միջոցները սահմանված են որպես գլոբալ անվանատարածքին վերաբերող։

Ստանդարտ գրադարանի հետևյալ ֆունկցիաների օգտագործման համար որևէ գլխագիր ֆայլեր միացնել հակավոր չէ․

void* operator new(std::size_t) throw(std::bad_alloc);
void* operator new[](std::size_t) throw(std::bad_alloc);
void operator delete(void*) throw();
void operator delete[](void*) throw();


Դիտումներ: 2267 | Ավելացրեց: hovo | - Վարկանիշ -: 5.0/2
Մեկնաբանություններն ընդամենը՝: 0
avatar