У рядках (16) та (20) викорисовубться функіії порівняння у стилі С. Останні функції порівняння (крім (1)) створювалися на основі заданих.
Розробка результуючого класу пов’язана з успадкуванням від двох абстрактних. Має наступну об’яву:
class clsString: public TPStrThread, public TPStrCompare
{ ··· }
Зауважимо, що базові класи не є віртуальними і немає наслідування від базового класу (як це зазначено у теоретичній частині). Виклики конструкторів будуть проходити так:
Конструктор за замовчуванням TPString
Конструктор за замовчуванням TPStrThread або TPStrCompare
Конструктор за замовчуванням TPStrCompare або TPStrThread
Конструктор clsString
Пункти 2 і 3 рівносильні і їх порядок залежить від компілятору (хоча в стандарті сказано, що вони викликаються в порядку об’яви).
В звязку з тим, що конструктори та операції присвоєння не наслідубться потрібно їх створювати зоново. Конструктори копіювання та перетворення аналогічні TPString. Розглянемо добавлені конструктори.
| (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) | clsString:: clsString (const long l) { char s [_CVTBUFSIZE]; if (_i64toa_s (l,s,15,10) ==EINVAL) code=1; else code=0; len=strlen (s); BuffLen=0; symb=NULL; setString (s); } clsString:: clsString (const double d, int pers) { char buf [_CVTBUFSIZE]; if (_gcvt (d,pers,buf) ! =0) code=1; else code=0; len=strlen (buf); BuffLen=0; symb=NULL; setString (buf); } |
У рядках (1) та (11) також об’явлені конструктори перетворення.
Цікавим є виділення пам’яті для тимчасового буферу, використовуючи _CVTBUFSIZE.
Згідно документації вона забезпечує саме той розмір, який необхідно для розміщення будь-якого число у строковому форматі, не залежно від системи (3) та (13).
Функція i64toa_s (4) забезпечує перетворення 64 бітного цілого на строку і у разі помилки повертає EINVAL.
Функція _gcvt (14) перетворює дійсне число у рідок символів і у разі помилки повертає її значення. (6) - (9) та (15) - (18) аналогічні розлянутим раніше.
Оператор () повертає підстроку але за типом класу:
| (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) | clsString& clsString:: operator () (int index, int subLen) { if (index<0 ||index>=len|| index+subLen>=len) return clsString (""); char *tempstr=new char [subLen+1]; if (subLen==0) subLen=len-index; strncpy (tempstr,symb+index,subLen); tempstr [subLen] ='\0'; clsString temp (tempstr); delete [] tempstr; return temp; } |
У (3) забзпечення коректності роботи алгоритму. (4) виділення тимчасовогу буферу.
У рядку (6) - копіювання підрядка, котрий у (8) передається у якості аргументу.
Далі (9) знищення тимчасового буферу та (10) повертання результату.
Функція пошуку першого входження реалізована за надійним але не найшвидшим алгоритмом О (n) =n*m, n-довжина базового рядка, а m-довжина рядка еквівалент якого шукаємо.
| (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) | int clsString:: find (const clsString& comp, int pos) { bool Notequal=1; if (comp. len>pos+this->len) return - 1; int fin=this->len-comp. len; for (; (pos<=fin) &&Notequal; pos++) { int k=0; for (int j=pos; k<comp. len; k++,j++) if (this->symb [j] ! =comp. symb [k]) break; if (k==comp. len) Notequal=0; } if (Notequal) return - 1; else return pos; } |
Флагова змінна Notequal (3) слугує для визначення: чи було знайдене вхождения. Змінна fin (5) вказує на кількість символім починаючи з яких необхідно виконати порівняння. (10) вийти з вложенного циклу, якщо знайдені неодинакові символи.
Результат проведення тестування:
| (Т1) (Т2) (Т3) (Т4) (Т5) (Т6) (Т7) (Т8) (Т9) (Т10) (Т11) (Т12) (Т13) (Т14) (Т15) (Т16) (Т17) (Т18) (Т19) (Т20) (Т21) (Т22) (Т23) (Т24) (Т25) (Т26) (Т27) (Т28) (Т29) (Т30) (Т31) (Т32) (Т33) (Т34) (Т35) (Т36) (Т37) (Т38) (Т39) | This program will test my work This is a small driver Testing in process This is a small driver Enter string Enter string Good day! This is a very good day! I have already done my work! Good day! This is a very good day! I have already done my work! s1 is "Testing in process" and s2 is "This is a small driver" The results of comparing is: s2==s1 yields false s2! =s1 yields true s2>s1 yields true s2<s1 yields false s2<=s1 yields false s2>=s1 yields true s1 += s2 yields s1 = Testing in processThis is a small driver s1 after s1 [0] = 't' and s1 [1] ='E' tEsting in processThis is a small driver find and delete in s1 s2 tEsting in process ********************************************************** s1 is "112211221122" and s2 is "334433443344334"Insert to s1 5 symbols from s2. Start position 0: 33443112211221122 Insert to s1 5 symbols from s2. Start position 5: 11221334431221122 Insert to s1 5 symbols from s2. Start position end of s1: 11221122112233443 2007 12.3456789012 Для продолжения нажмите любую клавишу... |
Тестування об’яви змінних, тобто конструкторів:
| (1) (2) (3) (4) (5) (6) (7) | clsString* temp=new clsString ("This program will test my work"); cout <<*temp<<endl; *temp= (clsString)"This is a small driver"; clsString test1 ("Testing in process"); clsString test2; clsString test3 (*temp); cout<<*temp<<endl<<test1<<endl<<test2<<endl<<test3; |
Создамо динамічний об’єкт (1) присвоївши значення "This program will test my work". Буде викликаний конструктор преведення типів. Перевіримо отримане значення (2), вивівши його в потік (чим і почнемо тестуваня введення/виведення). Далі виконаємо явне приведення типів (3), а також перевіримо оператор присвоєння. Крім того в покроковому режимі перевіримо роботу деструктора. Варіант створення статичної змінної (4) з початковим значенням, а із значенням за замовчуванням (5). Створення об’єкту на основі конструктора копіювання у рядку (6). Створені об’єкти у рядках (3) - (6) виведемо в потік (7). Результати роботи рядкі (1) - (7) є рядки на єкрані (Т1) - (Т5).
Далі протестуємо оператор введення.
| (1) (2) (3) (4) (5) (6) | cout<<"\nEnter string"<<endl; cin>>test2; cout<<test2<<endl; cout<<"Enter string"<<endl; cin>>test2; cout<<test2<<endl; |
У рядках (1) та (3) попросимо ввести рядок символів, що закінчується оператором переходу на новий рядок. Далі (2) та (4) вводимо ці рядки, та відразу ж показуємо введене. Має сенс ввести порожню строку, а потім непорожню. Результати у рядках (6) - (11).
Протестуємо оператори порівняння:
| (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) | cout<<"\ns1 is \""<<test1<<"\" and s2 is \""<< test3<<"\"" <<"\n\nThe results of comparing is: " <<"\ns2==s1 yields " << (test3==test1?"true": "false") <<"\ns2! =s1 yields " << (test3! =test1?"true": "false") <<"\ns2>s1 yields " << (test3>test1?"true": "false") <<"\ns2<s1 yields " << (test3<test1?"true": "false") <<"\ns2<=s1 yields " << (test3<=test1?"true": "false") <<"\ns2>=s1 yields " << (test3>=test1?"true": "false") <<endl; |
У рядку (1) вказуємо дві строки, які ми будемо порівнювати. У рядках (3), (5), (7), (9), (11), (13) ми говоримо користувачу, яку операцію будемо тестувати, а у наступномі виводимо результат відповідного тестування, використовуючи тернарну операцію. Результати (Т12) - (Т20).
Наступним етапом тестування стане операція конкатенації, процес її тестування подібний до операції тестування (Т23):
cout << "\n\ns1 += s2 yields s1 = ";
test1 += test3; // test overloaded concatenation
cout << test1<<endl;
Операція індексації матиме 2 тести: зміна 0 символу (відлік символів починається з 0), будь-якого іншого, наприклад 1, бо вказівник на рядок-вказівник на перший символ, а далі перевірка операції індексації (Т24) - (Т25):
test1 [0] ='t';
test1 [1] ='E';
cout<<"s1 after s1 [0] = 't' and s1 [1] ='E'"<<endl;
cout<<test1<<endl;
Функцію пошуку об’єднаємо з функцією видалення рядка:
| (1) (2) (3) | int pos=test1. find (test3); test1. TPdelete (pos,test3. lenght ()); cout<<test1<<endl; |
У змінну pos отримаємо позицію входження, потім (2) з цієї позиції видалимо весь рядок test3, використовуючи функцію визначення довжини рядка. Та виведемо (3) результат видалення (Т26) - (Т27).
Тестування операцій вставки більш складне:
| (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) | test1="112211221122"; test3="334433443344334"; cout<<"\ns1 is \""<<test1<<"\" and s2 is \""<< test3<<"\""; cout<<"Insert to s1 5 symbols from s2. Start position 0: "<<endl; test1. insert (test3,0,5); cout<<test1<<endl; test1="112211221122"; test3="334433443344334"; cout<<"Insert to s1 5 symbols from s2. Start position 5: "<<endl; test1. insert (test3,5,5); cout<<test1<<endl; test1="112211221122"; test3="334433443344334"; cout<<"Insert to s1 5 symbols from s2. Start position end of s1: "<<endl; test1. insert (test3,test1. lenght (),5); cout<<test1<<endl; |
Для полегшення перевірки вставки задамо початкові строки (1) - (2), (7) - (8), та (12) - (13).
Вставка в початок рядка (5), між 5 та 6 символами (10) та в кінець (13). Результати приведені у рядках (Т30) - (Т36).
На останок тестування конструкторів перетворення з числа до строки. Так як вони основані лице на стандартних операціях строк у стилі С, достатньо одного тесту на кожний:
| (1) (2) (3) (4) (5) (6) | temp=new clsString ( (long) 2007); cout<<*temp<<endl; temp->~clsString (); temp=new clsString (-12.34567890123); cout<<*temp<<endl; temp->~clsString (); |
У (1) та (4) створення об’єкту та передача значення. Замітимо явне приведення типу у (1), якщо цьго невиконати, то компілятор видасть помилку "error C2668: 'clsString:: clsString': ambiguous call to overloaded function". Це пов’язане з тим, що компілятор неявно не розрізняє ціле та дійсне числа, тому виника б неоднозначність. Ми використовуємо динамічні об’єкти, а отже виклик деструкторів (3) та (6) обов’язковий. Результат (2) та (3) є рядки (Т37) та (Т38).
В ході виконання курсової роботи були отримані наступні результати.