Лекции по обектно-ориентирано програмиране


Статични елементи на клас



страница12/16
Дата25.07.2016
Размер0.95 Mb.
#6568
ТипЛекции
1   ...   8   9   10   11   12   13   14   15   16

22. Статични елементи на клас.

Обикновено, всеки обект на клас има свое собствено копие на всички данни елементи на класа; в определени случаи, обаче, е нужно да има само едно копие на някои данни елементи за всички обекти на класа; за тези и за други цели се използват статични данни елементи, които съдържат информация за целия клас;
декларирането на статичните данни елементи започва с ключовата дума static; препоръчва се, с цел икономия на памет, ако е достатъчно единствено копие на данните да се използват статични данни елементи;

статичните данни елементи имат област на действие клас; те могат да бъдат public, private, protected;

на статичните данни елементи могат да се задават начални стойности само веднъж в областта на действие файл;
достъпът до откритите (public) статични елементи е възможен посредством всеки обект на класа или посредством името на класа, с помощта на бинарната операция за разрешаване на област на действие; вижда се, че откритите статичните данни елементи на клас са достъпни дори когато не съществуват обекти от този клас; в този случай достъпът до закритите и защитените статични елементи се осъществява с предвидена открита статична функция елемент, която се извиква, като се използва бинарната операция за разрешаване на област на действие;
една функция елемент може да бъде декларирана като static, ако тя не трябва да има достъп до нестатични елементи на класа, т.е. всяка статична функция елемент има достъп само до статичните данни елементи на класа; за разлика от нестатичните функции елементи, в статичните не може да се използва указател this, тъй като статичните данни елементи и статичните функции елементи съществуват независимо от обектите на класа; ключовата дума static се задава само в дефиницията на класа, но не и в описанието на статичната функция елемент;
типична грешка е използването на указателя this в статична функция елемент; друга типична грешка е деклариране на статична функция елемент като const, понеже контролът за const се осъществява точно от указателя this;
примерна програма:

#include

#include //използване на обслужваща функция assert

#include

class Employee

{ public:

Employee (const char *, const char *);

~Employee ();

const char *getFirstName () const;

const char *getLastName () const;

static int getCount (); //връща броя на създадените обекти

private:


char *firstName; char *lastName; static int count;

} ;


int Employee::count = 0;

//инициализиране на статичен елемент на клас в областта на //действие файл

int Employee::getCount () { return count;}

Employee::Employee (const char *first, const char *last)

{ firstName = new char [strlen(first) + 1];

assert (firstName != NULL);

strcpy (firstName, first);

lastName = new char [strlen(last) + 1];

assert (lastName != NULL);

strcpy (lastName, last);

count++;

cout << “Конструктор Employee за “ << firstName << ‘ ‘



<< lastName << endl;

}

Employee::~Employee ()



{ cout << “Деструктор Employee за “ << firstName << ‘ ‘

<< lastName << endl;

delete []firstName;

delete []lastName;

count--;


}

const char *Employee::getFirstName () const { return firstName;}

const char *Employee::getLastName () const { return lastName;}

//функциите връщат указатели към константа, за да се предотврати

//промяната на закритите данни елементи от клиента;

void main ()

{ cout << Employee::getCount () << endl;

Employee *e1ptr = new Employee (“Петър”, “Радев”);

Еmployee *e2ptr = new Employee (“Иван”, “Михайлов”);

cout << e1ptr->getCount() << endl;

cout << e1ptr->getFirstName() << ‘ ‘ << e1ptr->getLastName() << endl;

}
функцията assert е оператор за контрол; нейният прототип е описан в заглавния файл assert.h; тя проверява стойността на аргумента, който е израз; ако стойността на израза е 0, т.е. лъжа, assert извежда съобщение за грешка и извиква функция abort от библиотеката на стандартните функции, която завършва изпълнението на програмата; съобщението за грешка съдържа проверяваният израз, името на файла, съдържащ оператора за контрол и номера на реда, на който се намира този оператор;

прототипът на функцията abort е описан в stdlib.h, но за използването на assert, този файл не трябва да се включва;

операторите за контрол не трябва да се отстраняват от програмата след завършване на нейната проверка; когато операторите за контрол не са вече нужни, в началото на първичния файл с програмата (преди включване на assert.h) се включва директивата

#define NDEBUG

в резултат на това, препроцесорът игнорира всички оператори за контрол;


операция new автоматично разпределя памет за обект от съответния размер, извиква конструктора на обекта и връща указател към него; ако new не може да намери необходимата памет, тя връща указател

NULL;


операция delete освобождава паметта за обект, разпределена преди това с операцията new за същия обект; също така, операция delete автоматично извиква деструкторът на обекта;

23. Предефиниране на операции.

Манипулирането с обектите на класовете се реализира чрез изпращане на съобщение до обектите, т.е. чрез извикване на функции елементи за обектите; понякога тези обръщения са дълги и обемисти, особено за математически класове; в тези случаи е удобно да се използват наличните в C++ набори от вградени операции за работа с обекти, което се постига с помощта на механизма за предефиниране на операции;


например операцията с означение ‘<<’ се използва в C++ за различни цели – като операция за побитово отместване вляво или като операция за извеждане в поток; по същия начин, операцията ‘>>’ е операция за побитово отместване вдясно или операция за въвеждане от поток; всяка от тези операции е предефинирана в библиотеката от класове на C++;

друг пример за предефиниране на операции в C++ са операциите

‘+’ и ‘-‘ – те се изпълняват по различен начин при цели операнди, при операнди с плаваща точка или при адресна аритметика;

C++ не позволява да се създават означения за нови операции;

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

операциите се предефинират чрез функция с име, което се състои от ключовата дума operator и означението на операцията;

например operator+ е име на предефинираща функция за операцията за събиране;

за да се използва операция с обекти на класове, тази операция трябва да бъде предефинирана с две изключения:



  1. операцията за присвояване (=) може да се използва с всеки клас без явно да се предефинира; по премълчаване тази операция се свежда до побитово копиране на данните елементи на класа; такова побитово копиране, обаче, е опасно за класове с елементи указатели, които сочат към динамично разпределена памет; в такъв случай след присвояване на един обект на друг, те ще сочат към една и съща динамично разпределена област от паметта; изпълнението на деструктора на кой да е от тези обекти ще освободи тази област от паметта и ако след това чрез другия обект има обръщение към вече освободената памет, резултатът ще бъде неопределен; за такива класове явно трябва да се предефинира операцията за присвояване;

  2. операцията за адресиране (&) също може да се използва с обекти от всеки клас без да се предефинира; тя връща адреса на обекта в паметта; въпреки това тази операция може да се предефинира;

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


отделните компилатори имат особености при работа с предефинирани операции; повечето операции в C++ могат да бъдат предефинирани; не могат да бъдат предефинирани операциите за условен израз (?:), за разрешаване на област на действие (::), операцията ‘.’ за избор на елемент на обект и операцията sizeof;
приоритета и асоциативността на операциите не могат да бъдат променяни при тяхното предефиниране; при предефинирането на операции не могат да се използват аргументи по премълчаване; също така не е възможно да се промени броя на операндите на операциите – предефинираните унарни операции остават унарни, а предефинираните бинарни – бинарни; операциите ‘+’, ‘*’, ‘&’, ‘-‘ имат бинарен и унарен вариант и тези варианти се предефинират отделно;

предефинирането не може да промени начина на изпълнение на операциите с вградени типове; желателно е, поне един от операндите на предефинирана операция да е обект от тип, дефиниран от потребителя;

предефиниране на присвояването и събирането позволяват да напишем

object2 = object2 + object1

но това не означава, че операцията += също е предефинирана; за да можем да напишем

object2 += object1

това може да се постигне чрез явно предефиниране на операцията += за дадения клас; за да се осигури съгласуваност на такива свързани операции се препоръчва при предефинирането на едните да се използват другите; например при предефинирането на += да се използва операцията +, която вече е предефинирана;
в различни случаи предефиниращите функции е най-добре да бъдат приятелски функции или функции елементи или обикновени функции;

ако левият операнд на предефинираната операция трябва задължително да бъде обект на клас или псевдоним на обект на клас, тогава тя се предефинира с функция елемент; например операциите за извикване на функция ‘()’, за достъп до елемент на масив ‘[]’, указателната операция ‘->’ и операцията за присвояване ‘=’ се предефинират с функция елемент на клас;

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

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


примерна програма:

#include

class PhoneNumber

{ friend ostream &operator<< (ostream &, const PhoneNumber &);

friend istream &operator>> (istream &, PhoneNumber &);

private:


char areacode[4];

char exchange[4];

char line[5];

} ;


ostream &operator<< (ostream &output, const PhoneNumber &num)

{ output << “(“ << num.areacode << “) “ << num.exchange << ‘-‘



<< num.line;

return output;//осигуряваме слепване на извиквания

}

istream &operator>> (istream &input, PhoneNumber &num)



{ input.ignore ();

input.getline (num.areacode, 4);

input.ignore (2);

input.getline (num.exchange, 4);

input.ignore ();

input.getline (num.line, 5);

return input;//осигуряваме слепване на извиквания

}

void main ()



{ PhoneNumber phone;

cin >> phone;

//компилаторът заменя този оператор с operator>> (cin, phone);

PhoneNumber phone1, phone2;

cin >> phone1 >> phone2;//слепване на извиквания

cout << phone << endl;

//вторият оператор << е стандартно предефинирания

}
предефинираните операции трябва да имат ляв операнд от тип ostream (istream), например cout (cin), така че предефиниращите функции не могат да бъдат функции елементи на класа; тези функции се нуждаят от пряк достъп до закритите данните елементи на класа и затова се обявени като приятелски функции на този клас;


функцията ignore е функция елемент на класа istream, който е описан в iostream.h; тя игнорира указания брой символи от входния поток, по подразбиране игнорира един символ;

функцията getline е функция елемент на класа istream; тя прочита указаният брой символи и ги записва в низ, като автоматично добавя нулев байт;






Сподели с приятели:
1   ...   8   9   10   11   12   13   14   15   16




©obuch.info 2024
отнасят до администрацията

    Начална страница