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



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

24. Пример за клас масив.

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


#include

#include

class Array

{ friend ostream &operator<< (ostream &output, const Array &);

friend istream &operator>> (istream &input, Array &);

public:


Array (int = 10);

Array (const Array &);

~Array ();

int getSize () const;

const Array &operator= (const Array &);

int operator== (const Array &) const;

int operator!= (const Array &) const;

int &operator[] (int);

static int getArrayCount ();

private:


int *ptr;

int size;

static int arrayCount;

} ;


int Array::arrayCount = 0;

Array::Array (int arraySize)

{ arrayCount++;

size = arraySize;

ptr = new int[size];

assert (ptr != NULL);

for (int i = 0; i < size; i++) ptr[i] = 0;

}

Array::Array (const Array &init)



{ arrayCount++;

size = init.size;

ptr = new int[size];

assert (ptr!=NULL);

for (int i = 0; i < size; i++) ptr[i] = init.ptr[i];

}

Array::~Array ()



{ arrayCount--;

delete []ptr;

}

int Array::getSize () const



{ return size; }

const Array &Array::operator= (const Array &right)

{ if (&right != this)

{ delete []ptr;

size = right.size;

ptr = new int[size];

assert (ptr != NULL);

for (int i = 0; i < size; i++)

{ ptr[i] = right.ptr[i]; }

}

return *this;



}

int Array::operator== (const Array &right) const

{ if (size != right.size) return 0;

for (int i = 0; i < size; i++)

if (ptr[i] != right.ptr[i]) return 0;

return 1;

}

int Array::operator!= (const Array &right) const



{ return !(*this==right); }

int Array::getArrayCount ()

{ return arrayCount; }

int &Array::operator[] (int ind)

{ assert (ind>=0 && ind < size); return ptr[ind]; }

istream &operator>> (istream &input, Array &a)

{ for (int i = 0; i < a.size; i++)

input >> a.ptr[i];

return input;

}

ostream &operator<< (ostream &output, const Array &a)



{ int i;

for (i = 0; i < a.size; i++)

{ output << a.ptr[i] << ‘ ‘;

if ( (i+1) % 10 == 0) output << endl;

}

if (i % 10 != 0) output << endl;



return output;

}

void main ()



{ cout << Array::getArrayCount () << endl;

Array integers1 (7), integers2;

cout << integers1.getSize () << endl;

cout << integers1 << endl;

cin >> integers1 >> integers2;

cout << integers1 << integers2;

if ( integers1 != integers2)

cout << “Те не са равни!\n” << endl;

Array integers3 (integers1);

integers1 = integers2;

integers1[5] = 1000;

integers1[15] = 1000;

}
първият конструктор Array (int = 10) ще се извиква когато компилаторът срещне израз

Array integers (7);

еквивалентен запис е

Array integers = 7;

изразът

Array integers;

в този случай е еквивалентен на

Array integers (10);  Array integers = 10;


конструкторът Array (const Array &) се нарича конструктор за копие; той се извиква всеки път когато възникне необходимост да се копира обект – например при връщане по стойност на обект от функция, при предаване по стойност на обект към функция; този конструктор също така се извиква при инициализиране на обект, който да е копие, например:

Array integers2 (integers1);

еквивалентен запис е

Array integers2 = integers1;

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

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

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

integers1 = integers2;

компилаторът го заменя с

integers1.operator= (integers2);


възможно е да се забрани присвояването на един обект на друг; това се постига като предефинираща функция за операцията ‘=’ се опише като закрит елемент на класа; може да се забрани копирането на обекти – чрез описване на конструктора за копие и на предефиниращата функция за ‘=’ като закрити функции елементи;
при предефинирането на операцията [], резултатът е псевдоним на елемент от масива, което позволява този резултат да се използва като ляв операнд в присвояване; когато срещне израза

integers1[5] = 1000;

компилаторът го заменя с

integers1.operator[](5) = 1000;



25. Пример за клас низ.

в C++ няма вграден тип низ; със средствата на C++ може да се построи клас, който да управлява създаването и обработката на низове;


#include

#include

#include

#include

class String

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

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

public:


String (const char * = “”);

String (const String &);

~String ();

const String &operator= (const String &);

const String &operator+= (const String &);

int operator!() const;

int operator== (const String &) const;

int operator!= (const String &) const;

int operator< (const String &) const;

int operator> (const String &) const;

int operator<= (const String &) const;

int operator>= (const String &) const;

char &operator[] (int);

String operator () (int, int); //извличане на подниз

int getLength () const;

private:


char *sPtr;

int length;

} ;

String::String (const char *s)



{ length = strlen (s);

sPtr = new char[length+1];

assert (sPtr!=NULL);

strcpy (sPtr, s);

}

String::String (const String ©)



{ length = copy.length;

sPtr = new char [length+1];

assert (sPtr != NULL);

strcpy (sPtr, copy.sPtr);

}

String::~String ()



{ delete []sPtr; }

const String &String::operator= (const String &right)

{ if (&right != this)

{ delete []sPtr;

length = right.length;

sPtr = new char[length+1];

assert (sPtr != NULL);

}

return *this;



}

const String &String::operator+= (const String &right)

{ char *tempPtr = sPtr;

length += right.length;

sPtr = new char [length+1];

assert (sPtr != NULL);

strcpy (sPtr, tempPtr);

strcat (sPtr, right.sPtr);

delete []tempPtr;

return *this;

}

int String::operator! () const



{ return length == 0; }

int String::operator== (const String &right) const

{ return strcmp(sPtr, right.sPtr) == 0; }

int String::operator!= (const String &right) const

{ return strcmp(sPtr, right.sPtr) != 0; }

int String::operator< (const String &right) const

{ return strcmp(sPtr, right.sPtr) < 0; }

int String::operator> (const String &right) const

{ return strcmp(sPtr, right.sPtr) > 0; }

int String::operator<= (const String &right) const

{ return strcmp(sPtr, right.sPtr) <= 0; }

int String::operator>= (const String &right) const

{ return strcmp(sPtr, right.sPtr) >= 0; }

char &String::operator[] (int subs)

{ assert (subs >= 0 && subs < length);

return sPtr[subs];

}

String String::operator() (int index, int subl)



{ assert (index >= 0 && index < length && subl >= 0);

String sub;

if (subl == 0 || (index+subl > length))

sub.length = length – index;

else sub.length = subl;

//приема се, че при дължина на подниза 0 се взема низа до края

delete []sub.sPtr;

sub.sPtr = new char[sub.length+1];

assert (sub.sPtr != NULL);

strncpy (sub.sPtr, sPtr + index, sub.length);

sub.sPtr[sub.length] = ‘\0’;

return sub;//връща копие на sub

}

int String::getLength () const



{ return length; }

ostream &operator<< (ostream &output, const String &s)

{ output << s.sPtr;

return output;

}

istream &operator>> (istream &input, String &s)



{ char temp[100];

input >> setw (100) >> temp;

s = temp;

return input;

}
void main ()

{ String s1 (“happy”), s2 (“ birthday”), s3;

cout << s1 << s2 << endl << s3 << endl;

cout << (s1==s2) << endl << (s2 < s1) << endl;

if (!s3) cout << “s3 е празен низ!” << endl;

s3 = s1;


s1 += s2;

s1 += “ to you!”;

cout << s1 (0, 14) << endl;

cout << s1 (15, 0) << endl;

String *s4ptr = new String (s1);

*s4ptr = *s4ptr;

delete s4ptr;

s1[0] = ‘H’; s1[6] = ‘B’; s1[25] = ‘Y’;

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

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

в примера конструкторът преобразува тип char* в обект от клас String и това (заедно с предефинирата операция ‘=’) позволява на обект от клас String да се присвои масив от символи;

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

String s1 (“Поздрав!”);

String s1 = “Поздрав!”;

String s1; s1 = “Поздрав!”;
конструкторът за преобразуване позволява към обект от клас String да се приложи предефинираната операция ‘+=’ чрез низ от символи от тип char*;

ще отбележим, че при създаване на временен обект от клас String се извиква конструктора за преобразуване, а при неговото унищожаване – деструктора; аналогични разходи създава конструкторът за копие при предаване на обекти като параметри по стойност към функции или при връщане по стойност на обект като резултат от функция; това води до неефективност – в горната програма, например, предефиниращата функция за операция ‘+=’ може да приема направо аргумент от тип char*; от друга страна неявните преобразувания водят до по-малък обем на програмата и по-малко грешки в нея;


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

A::operator char* () const;

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

например ако s е обект от клас, то когато компилаторът срещне израза (char *) s, той генерира s.operator char *();

прототипите

A::operator int () const;

A::operator otherclass () const;

декларират предефиниращи функции на операции за преобразуване на обект от клас А в цял тип и в обект от друг потребителски тип otherclass;

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

например, ако операцията за преобразуване на обект от класа String към обект от тип char* беше определена, това позволява да не се предефинира операцията за извеждане в поток за този клас;






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




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

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