Помогни ни да направим Uroci.net по - богат! Добави урок
Категории Други уроци Adobe Photoshop Adobe Illustrator Adobe Flash Adobe Fireworks DreamWeaver CSS и HTML Corel Draw Image Ready PHP SEO CMS Microsoft Windows Microsoft Word Microsoft Excel PowerPoint Microsoft Access Microsoft Publisher Linux Visual basic JavaScript Ajax 3ds Max Maya 3D C++
C++
Sound Forge Gimp SWiSH

C++ част.7 (Член функции на клас)

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



Оператор ()

Един повторител позволява на потребителя да преглежда елементите, които се съдържат в обект на клас. Всяко извикване на повторителя връща “следващия” елемент докато не бъде прегледан всеки елемент. Следният програмен фрагмент илюстрира как може да бъде използуван един повторител за класа String:

String inBuf;
// read in a String object
while ( cin >> inBuf ) // iterate over the elements of inBuf
{ char ch;
while ( ch = inBuf() ) // ... do something with ch }

Този подраздел ще разгледа как този програмен фрагмент може да бъде реализиран чрез използуването на две оператор функции: не член функцията >>() и член функцията на String operator().

Операторът за вход е презареден за четене от обект на String по следния начин:

istream& operator>>( istream& is, String& s )
{ char inBuf[ STRING_SIZE ];
is >> inBuf;
s = inBuf; // String::Operator=( char * )
return is; }

Входният оператор функция на String трябва да присвоява char* на обект на String. Това може да бъде обработено чрез представител на String::Operator=(char*).

String& String::Operator=( const char *s )
{ len = strlen( s );
delete str;
str = new char[ len + 1 ];
strcpy( str, s );
return *this; }

Оператор функцията за обръщение () предлага един лесен начин за дефиниране на повторител за клас, който връща следващия елемент при всяко обръщение. След като бъде прегледан целия масив от елементи, повторителят връща 0. Това позволява използуването на конструкции като:

while ( ch = inBuf() ) // ... code

За реализирането на повторител на String е необходим един допълнителен член данни - index. index ще адресира следващия елемент, който трябва да бъде върнат. Всеки конструктор на String трябва да инициализира index с 0.

char String::Operator() ()
{ // provide for an iterator operator
if ( index < len ) return str[ index++ ];
// still here? completed iteration
return ( index = 0 );

}

Следната малка програма, която променя пунктуацията на String, използва повторителя, индексния оператор и потоковите оператори.

#include "String.h"
const LINESIZE = 40;
enum {PERIOD=’.’,COMMA=’,’,SEMI=’;’,COLON=’:’};

main() {
String inBuf( STRING_SIZE );
int lineSize = 0;
// operator>>( istream&, String& )
while ( cin >> inBuf ){
char ch;
int i = 0;

// String::Operator() ()
while ( ch = inBuf() ) { switch(ch)
{ case PERIOD: case COMMA: case SEMI: case COLON:

// String::Operator[](int);
inBuf[ i ] = ‘�’;
break;
}
++i;
++lineSize;
}

if ( lineSize >= LINESIZE )
{ cout << "n";
lineSize = 0;
}
cout << inBuf << " ";
}
cout << "n";
}

Програмата има следните входни данни:

We were her pride of ten; she named us: benjamin, phoenix, the prodigal, and perspicacious, pacific Suzanne. Benjamin, hush now. Be still, child. People are never just.

Когато тази програма бъде компилирана и изпълнена се получава следния изход:

We were her pride of ten she named us benjamin phoenix the prodigal and perspicacious pacific Suzanne Benjamin hush now Be still child People are never just

Оператори new и delete
По подразбиране отделянето на свободна памет за обекти на класове се осигурява от предварително дефиниран глобален представител на оператора new (разгледан в Раздел 5.1). Например, следната програма чете последователност от думи от терминала и ги подрежда по дължина. Тя използва класа, наречен StringList.

#include "String.h"
#include "StringList.h"
// maintain a pointer table indexed by length
const maxLen = 25;
StringList *stbl[ maxLen ];

main() {
// read in and sort strings by length
const inBuf = 512;
char st[ inBuf ] StringList *p;
while ( cin >> st )
{ p = new StringList( st );
int sz = p->getLen();
if ( sz >= maxLen ) // issue error message continue;
p->next = stbl[ sz ];
stbl[ sz ] = p;
}

for ( int i = maxLen - 1; i > 0; --i )
{ StringList *tp;
p = stbl[ i ];
while ( p != 0 )
{ cout << *p << "n";
tp = p;
p = p->next;
delete tp;
}} }

StringList дефинира два член данни, един обект на класа String, наречен entry и един указател към String, наречен next. Конструкторът получава един аргумент от тип char*. Този аргумент се изпраща на конструктора на String за инициализиране на entry.

StringList::StringList( char *s ) : entry( s ), next( 0 ) { }
getLen() е фуннкция за достъп. Тя връща дължината на entry.
StringList::getLen() { return entry.getLen(); }

Извиква се функцията за достъп на String() getLen() понеже член функциите на StringList нямат привилегии за достъп до не публичните членове на класа String. Неправилно би било представител на StringList да напише следното:

StringList::getLen() { // illegal: private member
retrun entry.len }

Операторът за изход е презареден да възприема псевдоним на StringList:

ostream& operator << ostream& os, StringLien& s )
{ retrun ( os << s.entry ); }

Входът на програмата е темата на този подраздел:

A class may provide new and delete operator functions

Когато програмата бъде компилирана и изпълнена се получава следния резултат:

functions operator provide delete class and new may A

След като вече сме сигурни, че програмата работи правилно можем да се опитаме да ускорим изпълнението й. Една вероятна област за подобрение е управлението на паметта. Понастоящем, new се извиква за отделяне на памет при всяко прочитане на дума. Ние бихме могли да променим това например, да отделяме памет за всеки 24 думи - така ще се получи значително ускоряване на програмата ни.

Един клас допуска собствено управление на паметта чрез задаване на оператори new и delete членове на класа. Ако са дефинирани, те се викат вместо представителите им по подразбиране. Програмите на потребителите не се нуждаят от промени.

Стратегията за управление на паметта, която ще използуваме за StringList, се състои в едновременно отделяне на памет за няколко обекта на StringList. Тези обекти ще бъдат организирани като свързан списък. Ето декларациите на freeStore и stringChunk:

class StringList { private:
enum { stringChunk = 24; }
static StringList * freeStore;
// ... };

stringChunk показва броя обекти на StringList, за които се отделя памет едновременно. freeStore е указател към свързания списък от налични обекти на StringList.

Един оператор new представител на член на клас трябва да дефинира тип на връщане void* и да получава първи аргумент от системния typedef size_t, дефиниран в системния заглавен файл stddef.h. Този аргумент автоматично се инициализира от компилатора с размера на типа на класа в байтове. Могат също така да бъдат дефинирани и допълнителни представители на оператора new, като всеки от тях да има уникална сигнатура. Раздел 5.3 разглежда презареждането на оператора new. Когато new се прилага към име на клас компилаторът проверява за наличието на собствен представител на класа. Ако такъв бъде намерен, той се избира; иначе се прилага предварително дефинирания глобален представител. Добавянето или отстраняването на представител new на класа не изисква промяна на текстовете на програмите.

Ето един пример представител на оператор new член на StringList. Той проверява дали има наличен обект от freeStore. Ако такъв има, той се подава от члена представител. Иначе, се извиква глобалния оператор new за отделяне на памет за stringChunk на брой обекти. Ето реализацията:

#include <stddef.h>
StringList *StringList::freeStore = 0;
void *StringList::Operator new( size_t size )
{ register StringList *p;
// if the free store is exhausted
// grab a new chunk of memory
if ( !freeStore )
{ long sz = StringChunk * size;
freeStore = p = new char[ sz ];
// the global new operator (StringList *)
// initialize the StringList freeStore

for ( ; p != &freeStore[ StringChunk-1 ]; p->next = p+1, p++ );
p->next = 0;
}
p = freeStore;
freeStore = freeStore->next;
return p; }

Член операторът delete възстановява обект на StringList към свързания списък от налични обекти. Ето реализацията му:

void StringList::Operator delete( void *p, size_t )
{// restore p to freeStore
((StringList*)p)->next = freestore;
freeStore = (StringList *)p; }

Операторът delete трябва да задава първи аргумент от тип void*. Като втори аргумент може да бъде зададен аргумент от системния тип typrdef size_t (не забравяйте да включите stddef.h). Ако е зададен, той неявно се инициализира с размера в байтове на обекта, адресиран от първия аргумент. Операторът delete трябва да дефинира тип за връщане void.

Представителят на клас на оператора new се извиква само за отделяне на памет за индивидуални обекти, а не за масиви от обекти. Например,

StringList *p = new StringList;

извиква представителя на new на StringList, докато

StringList *pia = new StringList[10];

извиква предварително дефинирания представител, който обработва отделянето на памет за масив от обекти от свободната памет. Забележете също, явното дефиниране на обект на клас, такова като

StringList s;

също не извиква представителя на оператора new.

Програмистът може по избор да извиква некласовия оператор new чрез оператора за обхват. Например,

StringList *ps = ::new StringList;

извиква оператора new по подразбиране. Съответно,

::delete ps;

извиква представителя на оператора delete по подразбиране. Оператор функциите new и delete са статични членове на класа си, и се подчиняват на обичайните ограничения за статичните член функции. В частност, да припомним, че статичните член функции нямат указвате this и следователно могат да имат само директен достъп до статичните член данни на класа си. (Виж раздел 7.6 относно статичните член функции). Тези оператори са направени статични член функции понеже се извикват или преди конструирането на обект на клас (new) или след разрушаването му (delete).

X::Operator=(const X&)

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

Компилаторът генерира представител на оператора за присвояване на класа със следната форма:

X& X::Operator=( const X& );

за да обработва по подразбиране почленовото присвояване на обекти на клас. Например, дадени са следните два обекта на String:

String article( "the" );

String common( "For example" );

и оператора за присвояване common = srticle; който се обработва чрез явния почленов оператор за присвояване:

String& String::Operator=( const String& s ) {
len = s.len;
str = s.str;
index = s.index;
}

Съществуват няколко проблема, свързани с присвояването.

1. Както и при почленната инициализация article и common сега адресират една и съща област от свободната памет. Деструкторът на двата обекта на String ще бъде приложен към тази единствена област.

2. Паметта, отделена за да съдържа For example никога не се освобождава. Тя се загубва по време на почленното присвояване.

3. Проектът на значенията на класа String забранява простото копиране на всеки член данни. В раздел 7.3 дефинирахме член данни index на класа String, който позволява на потребителя да преглежда символния масив на String. Стойността на index след почленното копиране трябва да бъде 0 за обекта назначение от класа String - не е така, обаче, за обекта източник. Почленното копиране по подразбиране нарушава семантиката на повторителя за String.

Проектантът на класа може да разреши тези проблеми като предложи явен представител на оператора за почленно присвояване. Представителите на класа String може да бъдат дефинирани по следния начин:

String& String::Opperator=( const String& s )
{ index = 0;
len = s.len;
delete str;
str = new char[ len + 1 ];
strcpy( str, s.str );
return *this; }

StringList, дефиниран в предишния раздел, съдържа член на класа String, наречен entry. StringList не дефинира оператор за почленно присвояване. Когато един обект на StringList се присвоява на друг обект от класа StringList, явно се извиква оператора за почленно присвояване за да обработи почленното копиране на entry. Например,

#include "StringList.h"
main() {
StringList sl1( "horse" );
StringLIst sl2( "carriage" );
// sl1.next = sl2.next
// sl1.entry.String::Operator=(sl2.entry) sl1 = sl2;
}

Ако StringList дефинира собствен оператор за почленно присвояване, представителят на String ще бъде викан само ако в тялото на оператора явно присъствува присвояване на Stirng. Например,

StringList& StringList::Operator=( const String& s ) {
// String::Operator=(const String&)
entry = s.entry;
next = 0;
return *this; }

Ако присвояването

entry = s.entry;

не е написано, няма да бъде извикан почленния оператор - entry няма да бъде променен.

Често инициализацията и присвояването не се разграничават адекватно от програмистите, които реализират типовете класове. Това понякога може да предизвика неефективност на реализацията на класовете. Например, дадени са следните две дефиниции на клас:

class X { public:
X();
X( int );
X( const X& );
X& operator=( const X& );
// ... class Y { public:
Y();
private:
X x;
};

като следната проста реализация на конструктора на класа Y:

Y::Y() { x = 0; }

предизвиква явно извикване на два конструктора на X плюс извикване на оператора за присвояване за X:

1. Конструктора по подразбиране

X::X() се

извиква преди конструктора на Y за да инициализира обекта x член на клас. Присвояването на стойност 0 на x не може да бъде извършено директно понеже класът X не дефинира оператор за присвояване, който да получава аргумент от тип int. Присвояването се извършва на два етапа:

2. Извиква се конструктора

X::X( int );

за да преобразува цялото число 0 в обект от тип X. (Използуването на конструктори за преобразуване на типовете се разглежда в раздел 7.5 по нататък в тази глава).

3. Този новосъздаден обект на класа X се присвоява на X чрез извикването на

X::Operator=( const X& )

Второто и третото извикване са ненужни и няма да бъдат направени ако конструкторът на Y инициализира подходящо члена x:

Y::Y() : x( 0 ) {}

Сега ще бъде извикан само X::X( int ) при всяко извикване на конструктора на класа Y.

Оператор ->

Операторът за избор на член (“->”) се обработва като унарен оператор на левия си операнд, който трябва да бъде или обект на клас или псевдоним на такъв обект. Стойността за връщане на оператор функцията трябва да бъде указател към обект на клас или обект на клас, за който операторът -> e дефиниран. В следния пример е деклариран един представител, който връща String*.

#include "String.h"

class Xample {
public: String *operator->();// ...
private: String *ps; // ... };

Eто една схематична реализация на оператора. Той връща члена ps след като направи някаква обработка на обекта, който сочи.

String* Xample::Operator->()
{ if ( ps == 0 ) // ... initialize ps
// ... process ps
return ps; }

Операторът за избор на член може да бъде извикан за обект или за псевдоним на обект на класа Xample. Например,

void ff( Xample x, Xample &xr, Xample *xp )
{ int i;
// invoke String::getLen()
i = x->getLen(); // ok: x.ps->getLen()
i = xr->getLen(); // ok: xr.ps->getLen()
i = xp->getLen(); // error: no Xample::getLen()

Операторът за избор на член не може да бъде извикан чрез указател към Xample понеже компилаторът не може да разграничи предварително дефинирания и презередимия представител.

Проектиране на презаредим оператор функция

Предварително дефинираните оператори за клас са само оператора за присвояване (“=”) и адресния оператор (“&”). Всеки друг оператор може да има значение когато се прилага към обект на клас ако проектантът на класа явно го дефинира. Кой оператор трябва да бъде избран се определя от потребителя на класа.

Винаги започвайте с дефиниране на публичния интерфейс на класа. Какви оператори трябва да предлага класа на потребителите си? Това ще бъде минималния набор то публични член функции. След като този набор бъде дефиниран е възможно да се разгледа кои оператори трябва да предлага типа клас. Всеки оператор се свързва с някакво значение от предварително дефинираното му използуване. Бинарният +, например, се отнася за събирането. Съпоставянето на + на някоя аналогична операция за типа клас може да предложи удобен кратък запис. Например, конкатенацията на обекти на String е едно подходящо разширение на +.

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

operator!();
isEqual става за оператор за равенство,
operator==();
copy() става за оператор за присвояване,
operator=();

и т.н.

Операторите за еквивалентност трябва също да бъдат дефинирани явно за един клас. Операторите на String, например, поддържат конкатенация и почленово копиране:

String s1( "C" );
String s2( "++" );
s1 = s1 + s2; // s1 <== "C++"

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

s1 += s2;

Този оператор също трябва явно да бъде дефиниран и да предлага еквивалентна семантика.

Упражнение 7-15. Определете кои член функции на класа Screen, дефиниран в Глава 6 могат да бъдат презаредени чрез оператори.
Упражнение 7-16. Реализирайте презаредимите оператор функции, зададени от предишното упражнение.
Упражнение 7-17. В раздел 4.2 беше дефиниран клас - списък от цели числа, IntList. Определете и реализирайте презаредимите оператор функции за този клас.

Един клас бинарно дърво трябва да предлага следните функции: isEmpty(), isEqual(), print(), addNode(), build(), copy() и deleteNode().

Упражнение 7-18. Кои от тези член функции са кандидати за презареждане? Кои от тях са кандидати за презаредими оператори?
Упражнение 7-19. Реализирайте член функциите на бинарното дърво, изброени в горния параграф.
Упражнение 7-20. INodes се генерират доста често при работа с BinTree. Напищете нов начин за управление на паметта, като използувате операторите new и delete.



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

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

реклама

© Всички права запазени. 2006-2008. Created by: Site.bg
Препоръчваме: IT Новини | Кино и игри | Диплома.бг | Paparak.bg | Тунинг Портал | uchenik.com | TRAVEL туризъм | Реферати | AmAm.bg | Иде.ли | Курсови работи | AnimeS-bg.com | Фото Форум | Запознанства | Мрежа от приятели | IT Light | Spodeli.net | Фото-Култ | IDG.BG | Teenproblem.net | Блог - Образование | Fresh-BG.com | Hanovete.com | Bulfleet.com | Mythlands.com | Ohoboho.com | Казанлък.Com | News24 | Setcom.bg | Atol.bg | Elmaz.com | MobileBulgaria.com