A Managed C++ programozási nyelv

Felügyelt típusok

A felügyelt típusok elérhetővé teszik a CLR szolgáltatásait, így élvezik annak előnyeit, de ugyanakkor bizonyos megszorítások is vonatkoznak rájuk. Minden felügyelt típus, illetve osztály egyetlen közös ősosztályból származik: az Object-ből.

Érték típusok

A globális és lokális változók használatához a CLR támogatja az érték típusokat, amelyeket a __value kulcsszóval definiálunk. Az érték típusú objektumok mindig a veremben foglalnak helyet; nem lehet őket a new operátorral a dinamikus tárterületen elhelyezni. Ennek megfelelően az érték típusokból csak globális vagy lokális változót lehet létrehozni.

A beépített típusok, mint például az int, a short, a bool és a char, érték típusok, ezért esetükben a __value elhagyható.

A beépített típusok:

Típus Csomagoló osztály Min. érték Max. érték Leírás
short System::Int16 -32768 32767 16 bites előjeles egész
int System::Int32 -2 147 483 648 2 147 483 647 32 bites előjeles egész
long System::Int64 -9 223 372 036 854 775 808 9 223 372 036,854 775 807 64 bites előjeles egész
unsigned short System::UInt16 0 65535 16 bites előjel nélküli egész
unsigned int System::UInt32 0 4 294 967 295 32 bites előjel nélküli egész
unsigned long System::UInt64 0 18 446 744 073 709 551 615 64 bites előjel nélküli egész
bool System::Boolean false true Logikai típus
__wchar_t System::Char 0x0000 0xFFFF Unicode karakter
unsigned char System::Byte -128 127 Előjeles bájt
char System::SByte 0 255 Előjel nélküli bájt
float System::Single -3.402823e38 3.402823e38 Előjeles 32 bites lebegőpontos valós
double System::Double -1.79769313486232e308 1.79769313486232e308 Előjeles 64 bites lebegőpontos valós

A fenti típusok az alábbi interfészeket implementálják: IComparable (összehasonlítható), IFormattable (formázható), és IConvertible (konvertálható). Mindegy, hogy deklaráláskor a hagyományos C++-beli típuselnevezést használjuk vagy az elfedő osztály nevét. Az elfedő osztályok különböző hasznos metódusokat tartalmaznak, úgy, mint:

Struktúrák. Ezt a típust a C++-ban megszokottak szerint használjuk, annyi különbséggel, hogy a egy plusz __value kulcsszó kerül a struct elé:

__value struct ExampleValueType {}; ExampleValueType globalVar; int main() { ExampleValueType localVar; }

Az érték típusokat csak a felügyelt tárterületen nem lehet dinamikusan létrehozni. Az _nogc kulcsszóval az érték típú változók elhelyezhetők a felügyeletlen dinamikus tárterületen.

void Func() { ExampleValueType* pVar = __nogc new ExampleValueType; }

A módszer hátulütője, hogy a virtuális gép elveszti a szemétgyűjtés lehetőségét, mivel a felügyeletlen területet definíció szerint nem tudja felügyelni. Ezért a fenti kód esetén fennáll a memórielszivárgás veszélye. A megoldás olyan esetekben szükséges, amikor a programban felügyeletlen entitásokat kell használni. A new operátor __nogc-os verziója természetesen nem használható nem érték típusoknál.

Referencia típusok

Tömbök. Minden felügyelt tömb a System::Array osztályból származik. Tömbök deklarációja érték típusok esetén eltér a C++-tól. Általában: Típus Azon __gc[méret]. A __gc kucsszót elhagyva felügyeletlen tömböt kapunk. Felügyelt referencia típus esetén nem kötelező a kulcsszó használata.

Példa egy egészeket tartalmazó egydimenziós tömbre:

int vIntegers __gc[]; vIntegers = new int __gc[10];

Többdimenziós tömb esetén is más a szintaktika. Általában: Típus Azon __gc[,,..,], ahol a vesszők száma a dimenziót adja meg.

int vIntegers2 __gc[,,]; vIntegers = new int __gc[10,5,3];

Az indexelés sem egyezik a megszokottal a többdimenziós esetben:

int n = vIntegers2[1, 2, 3]; //nem pedig vIntegers[1][2][3]

Dobozolás

Gyakran van szükség arra, hogy a CLR dinamikus tárterületén hozzunk létre egy érték típusú változót. Ez megtehető például úgy, ha az érték típust becsomagoljuk egy referencia típusú osztályba, és annak az objektumait helyezzük el felügyelt memóriában. Mivel gyakran szükség lehet erre, a nyelv egy programozóbarátabb megoldást is kínál a probléma megoldására, melynek neve dobozolás (boxing).

A dobozolás elkészíti egy érték típusú objektum felügyelt másolatát és a CLR heapjébe helyezi. Ehhez a __box kulcsszót kell használni, amely bitenként átmásolja az objektumot a felügyelt objektumba, ami System::ValueType típusú (azaz leszármazottja a System::Object-nek). Az új felügyelt objektum címét adja vissza. Fontos: a létrehozott másolaton a változtatások nem hatnak vissza az eredeti érték típusú objektumra.

Példa:

__value struct V { int i; }; void Positive( Object* ) { } // egy felügyelt osztályt vár int main() { V v = { 10 }; // lefoglalás és inicializálás __box V* pBoxedV = __box( v ); // másolás a CLR heapbe Positive( pBoxedV ); // itt már felügyelt osztályként kezeli pBoxedV->i = 20; // a dobozolt verzió módosítása return 0; }

A dobozolás viszonylag drága mulatság, és amíg C#-ban implicit módon történik, Managed C++-ban explicite jelezni kell ezt a szándékunkat. (Teljesítménybeli megfontolások miatt van így.)

Egy érték típusú osztály dobozolt példányát a __dynamic_cast, vagy a __try_cast kulcsszavak használatával kaphatjuk meg. Ezek egy mutatóval térnek vissza, amelyet dereferálva visszakapjuk az érték típusú objektumot. Az eljárást kidobozolásnak hívják.

Példa:

__value struct V { int i; }; int main() { V v = { 10 }; Object* o = __box( v ); // az érték másolása a CLR heapbe V v2 = *dynamic_cast<__box V*>( o ); // visszamásolás a CLR heapből }

Felügyelt osztályok, interfészek

A felügyelt osztályok főbb tulajdonságai:

Főbb jellegzetességek Főbb megszorítások
  • A __gc class kulcsszóval kell deklarálni.
  • Lehet olyan adattagja, ami felügyeletlen típusú objektumra mutató pointer.
  • Lehet felhasználó által definiált destruktora.
  • A delete operátor alkalmazható az osztályra mutató pointerre, ennek hatására a destruktor azonnal meghívódik.
  • Megvalósíthat bármennyi felügyelt interfészt (__interface).
  • Lehetnek tulajdonságai (__property).
  • Megjelölhetjük az __abstract kulcsszóval.
  • Az osztályt zárolhatjuk (__sealed).
  • Lehet statikus konstruktora.
  • Lehet konstruktora.
  • Lehetnek láthatósági szabályzói (public, protected, private).
  • Nem származtatható felügyeletlen osztályból.
  • Felügyeletlen osztály nem származtatható belőle.
  • Legfeljebb egy (!) felügyelt osztályból származtatható.
  • Nem lehet felhasználó által írt másoló konstruktora.
  • Nem deklarálhat és nem definiálhat barát osztályokat és függvényeket.
  • Nem deklarálhat és nem definiálhat new vagy delete operátort.
  • Nem tartalmazhat using deklarációt.
  • Nem lehet konstans (const) tagfüggvénye.
  • Ha nem adjuk meg az ősosztályát, akkor automatikusan a System::Object-ból származik.

Zárolt (__sealed) osztályok. A zárolt osztályokból nem lehet más osztályokat származtatni. Taggfüggvényeket is elláthatunk ezzel a kulcsszóval: a zárolt tagfüggvényeket nem lehet felüldefiniálni az osztály leszármazottaiban. A kulcsszó csak felügyelt osztályokra alkalmazható, felügyeletlen osztályokra és interfészekre nem. (Természetesen, hiszen mi értelme volna egy olyan interfésznek, amiből nem lehet származtatni?)

Absztrakt (__abstract) osztályok. Az absztrakt osztályok a tervezés eszközei. Nem lehet példányosítani őket, csak leszármazottaikat. Ennek értelmében tehát zárolt osztályokra nem alkalmazható (mivel azokból nem lehet származtatni).

Felügyelt interfészek. Olyanok mint az osztályok, csak minden tagfüggvényük absztrakt, és nem tartalmazhatnak implementációt. Az __interface kulcsszóval vezethetjük be őket. Lehet bennük érték felsorolási típus (__value enum). Az interfészekben nem kell explicite jelölni, hogy egy tagfüggvény virtuális (a virtual kulcsszóval) és absztrakt (az = 0 szuffixummal), mivel nem is lehet más.

Főbb megszorításai: