Помогни ни да направим Uroci.net по - богат! Добави урок

C++ част.6 (Класове)

C++ » C++
fix3d   трудност:    видян: 44654



6.9. Обединение: Клас, който пести памет

Обединението е специален представител на клас. Количеството памет, отделено за едно обединение, е количеството, необходимо за най-големия му член данни. Всеки от членовете започва от един и същ адрес на паметта. Във всеки един момент може да бъде присвоена стойност само на един член. Например, лексичният анализатор на компилатора разделя потребителската програма на последователност от единици. Операторът,

int i = 0;

се преобразува в последователност от пет единици:

1. Ключовата дума за тип int.

2. Идентификаторът i.

3. Операторът =.

4. Цялата константа 0.

5. Разделителят ;

Тези единици се преглеждат от лексичния анализитор по време на синтактичния разбор. Първата стъпка на синтактичния анализатор е да определи последователността от единици. Информацията трябва да бъде представена така, че синтактичният аналазатор да може да разпознава потока от единици като декларации - например, като следната последователност:

Type ID Assign Constant Semicolon

След като синтактичният анализатор вече е разпознал общата последователност от единици той се нуждае от определена информация за всяка единица. В нашия случай той трябва да знае, че

Type <==> int

ID <==> i

Constant <==> 0 Той не се нуждае от друга допълнителна информация относно Assign и Semicolon.

Представянето на една единица, следователно, изисква два члена token и value. token ще съдържа един уникален номер, който се присвоява на всички възможни единици. Например, идентификаторите могат да бъдат представени чрез 85, а точка и и запетая чрез 72. value ще съдържа някаква информация за представителя на единицата. Например, за ID, value ще съдържа низът i; за Type - кодовото представяне на типа int.

Представянето на value е проблематично. Въпреки, че value трябва да съдържа само една стойност за всяка дадена единица, тази стойност може да има различен тип данни. Едно подходящо представяне за тези типове данни, разбира се, е клас. Компилаторът може да декларира value от тип TokenValue като дефинира TokenValue да съдържа член за всички възможни типове данни на value.

Такова представяне решава проблема. value, обаче, може да бъде само от един от възможните типове данни за всяка конкретна единица. TokenValue, обаче, ще отдели необходимата памет за всички възможни типове данни. По-добре би било, обаче, TokenValue да поддържа паметта, необходима само за един от възможните типове данни, а не за всичките заедно. Обединението позволява това. Ето дефиницията на обединението TokenValue:

union TokenValue

{ char cval;

char *sval;

int ival;

double dval; };

Тъй като най-много памет трябва да бъде отделена за типа данни double, размерът на TokenValue е същия като този на double. По подразбиране членовете на обединението са публични. Едно обединение не може да съдържа статични член данни или членове обекти на клас, който дефинира конструктор или деструктор. Ето един пример за това как TokenValue може да бъде използуван:

class Token

{ public:

int tok;

TokenValue val; }

Един обект от тип Token може да бъде използуван по следния начин:

lex() {

Token currToken;

char *curString;

int curIval;

// ...

case ID: // identifier

curToken.tok = ID;

curToken.val.sval = curString;

break;

cаse ICON: // integer constant curToken.tok = ICON;

curToken.val.ival = curIval;

break;

// ...etc. };

Опастността при използуване на обединение се състои в това, че е възможно случайно възстановяване на текущата стойност на обединението чрез неподходящ член данни. Например, ако при последното присвояване на стойност на обединението е използувана цяла стойност ival, програмистът не би желал да възставови тази стойност чрез указател към низ от символи sval. Ако това бъде направено вероятно ще предизвика програмна грешка.

За предотвратяване на този тип грешки, обикновено се дефинира една допълнителна променлива, която да поддържа типа на стойността, текущо съхранена в обединението. Тази допълнителна променлива се нарича дескриптор на обединението. За това служи члена tok на Token. Например,

char * idVal;

if ( curToken.tok == ID ) idVal = curToken.val.sval;

Съществува една добра традиция, когато се обработва обект от тип обединение като част от даден клас да се създаде набор от функции за достъп до всеки елемент на обединението. Например,

char *Token::getSting() {

if ( tok == ID ) idVal = curToken.val.sval;

error( ... );

}

Името на обединението е опционно. Няма никаква причина да бъде предлагано такова име ако то ще бъде използувано само от един представител. Когато такова име не бъде предложено то в програмата има един глобален идентификатор по-малко и съответно намаляват възможностите за възникване на колизии. Например, следната дефиниция на Token е еквивалентна на предходната дефиниция. Единствената разлика е, че обединението няма име:

class Token

{ public:

int tok;

union

{ char cval;

char *sval;

int ival;

double dval;

}

val; }

Съществува един специален представител на обединение, наречен анонимно обединение. Анонимното обединение е обединение без име и не е следвано от дефиниция на обект. Например, ето една дефиниция на класа Token, която съдържа анонимно обединение:

class Token

{ public:

int tok;

union // anonimous union

{ char cval;

char *sval;

int ival;

double dval;

}; };

Член данните на един анонимен клас са достъпни директно. Например, ето фрагмента lex(), написан така, че да използува дефиницията на Token, която съдържа анонимно обединение:

lex() {

Token curToken;

char *curString;

int curIval;

// ... figure out what the token is

// ... now set curToken

case ID: curToken.tok = ID;

curToken.sval = curString;

break;

case ICON: // integer constant curToken.tok = ICON;

curToken.ival = curIval;

break;

// ... etc. }

Всяко анонимно обединение отстранява едно ниво за избор на член понеже имената на членовете на обединението влизат във външния обхват. Едно анонимно обединение не може да има лични или защитени членове. Анонимно обединение, дефинирано с файлов обхват трябва да бъде декларирано като static.



6.10. Разредно поле: член, който пести памет

Един специален член данни на клас, наречен разредно или битово поле, се състои от определен брой битове. Той може да бъде със или без знак. Например,

class File { // ...

unsigned modified : 1; // bit field };

След идентификатора на едно битово поле се записва двуеточие и константен израз, който определя брой на битовете. modified, например, е разредно поле, което се състои от един бит.

Разредни полета, дефинирани в последователен ред в тялото на класа, ще бъдат пакетирани, ако е възможно, в съседни битове като едно цяло число, като чрез това се оситурява компактност на съхраняването. Например, в следната декларация, петте разредни полета са записани на място, отделно за едно unsigned int, което е свързано първо със съвкупността от битови полета.

typedef unsigned int Bit;

class File { public:

// ...

private:

Bit mode: 2;

Bit modified: 1;

Bit prot_owner: 3;

Bit prot_group: 3;

Bit prot_word: 3; // ... };

Едно разредно поле е достъпно по същия начин, както и останалите член данни на даден клас. Например,

File::write() { modified = 1;// ... }

File::close() { if ( modified ) // ... save contents }

Ето един прост пример за това как може да бъде използувано битово поле по-голямо от един бит (Раздел 3.8 операциите за работа с битове, използувани в примера):

enum { READ = 01, WRITE = 02 }; // File modes

main() {

File myFile;

myFile.mode |= READ;

if ( myFile.mode & READ )

cout << "nmyFile.mode is set to READ";

}

Обикновено се дефинира едно множество от член функции inline за да се проверява стойността на члена разредно поле. Например, File може да дефинира isRead() и isWrite().

inline File::isRead() { return mode & READ; }

inline File::isWrite() { return mode & WRITE; }

if ( myFile.isRead() ) /* ... */

Операторът адрес-на (“&”) не може да се прилага към разредни полета, и поради това не могат да се дефинират указатели към разредни полета на класове. Нито пък битовите полета могат да бъдат декларирани като static.


Страници: «6 7 8 9 10 »

Сподели урока:



Коментари (1)

odissea на 19.02 2009 в 17:32ч.
добро - но прекалено дълго ... отказах се да го чета до края

Регистрирайте се, за да добавите коментар


Калдейта Ком ЕООД - © 2003-. Всички права запазени.
Препоръчваме: IT Новини