Клас-лічильник рядків
Припустимо, що існує кілька об’єктів класу String, що вказують на певний рядок, і ми хочемо навчити програму підраховувати, скільки саме об’єктів на нього вказує. Де нам зберігати цей лічильник?
Для кожного об’єкту класу String було б надто обтяжливо підраховувати, скільки його колег вказує на даний рядок, тому ми не будемо вводити лічильник до складу полів класу String. Наступна ідея – використати статичну змінну, точніше, статичний масив, який зберігав би список адрес рядків та їх порядкових номерів. Але ще раціональніше було б створити новий особливий клас для підрахунку рядків та вказівників. Кожен об’єкт такого класу – назвімо його strCount – буде містити лічильник і сам вказівник на рядок. В кожен об’єкт класу String помістимо вказівник на відповідний об’єкт класу strCount. Щоб переконатися у нормальному доступі об’єктів String до об’єктів strCount, зробимо String дружнім по відношенню до strCount. Крім того, нам хотілося б достовірно знати, що клас strCount використовується тільки класом String. Щоб заборонити несанкціонований доступ до будь-яких його функцій, зробімо всі методи strCount прихованими. Оскільки String є дружнім, на нього це обмеження не поширюється.
Далі приведений лістінг програми 22.17
#include<iostream.h>
#include<conio.h>
#include<stdio.h>
#include<bios.h>
#include<string.h>
/////////////////
class strCount
{private:
int count;
char* str;
friend class String;
strCount(char* s)
{int length=strlen(s);
str=new char[length+1];
strcpy(str,s);
count=1;
}
~strCount()
{delete[] str;}
};
/////////////////
class String
{private:
strCount* psc;
public:
String()//конструктор без аргументів
{psc=new strCount("NULL");}
//-------
String(char* s)//конструктор з 1 аргументом
{psc=new strCount(s);}
//-----------
String(String& S) //конструктор копіювання
{psc=S.psc;
(psc->count)++;}
//----------
~String() //деструктор
{if(psc->count==1)
delete psc;
else
(psc->count)--;
}
//----------
void display()
{cout<<psc->str;
cout<<" (addr="<<psc<<")"; //виведення адреси
}
//-----------
void operator=(String& S)
{if(psc->count==1)
delete psc;
else
(psc->count)--;
psc=S.psc;
(psc->count)++;
}
};
//////////////
int main()
{clrscr();
String s3="Яка-небудь пробна фраза";
cout<<"\ns3=";s3.display();
String s1;
s1=s3;
cout<<"\ns1=";s1.display();
String s2(s3);
cout<<"\ns2=";s2.display();
cout<<endl;
bioskey(0);
return 0;
}
Програма 22.17
В частині main() даної програми ми визначаємо об’єкт класу String s3, що містить яку-небудь пробну фразу. Потім визначаємо ще один об’єкт s1 і присвоюємо йому значення s3. Визначаємо s2 та ініціалізуємо його за допомогою s3. При цьому ми спершу запускаємо перезавантажену операцію присвоювання, а потім – перезавантажений конструктор копіювання. Потім виводимо всі три рядки, а також адресу об’єкту класу strCount, на який посилається вказівник кожного об’єкту. Ми це робимо, щоб показати, що всі рядки насправді представлені тим самим рядком.
Інші обов’язки класу String ми поділили між класами strCount і String.
Клас strCout містить вказівник на реальний рядок і підраховує, скільки об’єктів класу String на нього вказують. Його єдиний конструктор розглядає вказівник на рядок в якості аргументу і виділяє для нього область пам’яті.
str=new char[length+1];
Він копіює рядок в цю область і встановлює лічильник в одиницю, оскільки лише один об’єкт String вказує на цей рядок відразу ж після створення.
strcpy(str,s);
count=1;
Деструктор класу strCount вивільняє пам’ять, зайняту рядком. Ми використовуємо delete[] з квадратними дужками, оскільки рядок – це масив.
Натомість у класу String є 3 конструктори. При створенні нового рядка генерується новий об’єкт strCount для його зберігання, а вказівник psc зберігає посилання на цей об’єкт. Якщо копіюється вже існуючий об’єкт String, вказівник psc продовжує вказувати на старий об’єкт strCount, а лічильник в цьому об’єкті збільшується. Перезавантажена операція присвоювання повинна нарівні з деструктором видаляти старий об’єкт strCount, на який вказує psc, якщо лічильник рівний одиниці. (Тут нам квадратні дужки після delete вже непотрібні, оскільки ми видаляємо єдиний об’єкт strCount). Але чому оператор присвоювання повинен займатися видаленням? Справа в тому, що об’єкт String, що стоїть у виразі зліва від знаку рівності (назвемо його s1), вказував на певний об’єкт strCount (назвемо його oldstrCount). Після присвоєння s1 буде вказувати на об’єкт, що знаходиться справа від знаку рівності. А якщо більше не існує об’єкту String, що вказують на oldstrCount, він повинен бути видалений. Якщо ж ще залишилися об’єкти, що посилаються на нього, його лічильник повинен бути зменшеним.
if(psc->count==1)
delete psc;
else
(psc->count)--;
Дата добавления: 2015-08-26; просмотров: 571;