#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* беше определена, това позволява да не се предефинира операцията за извеждане в поток за този клас;
Сподели с приятели: