ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 11) онлайн
Исправленная программа STRIMEM
Используя указатель this, можно переделать функцию operator=() в программе
STRIMEM и заставить ее возвращать результат по ссылке. Таким способом можно
осуществить множественные присваивания для класса String, например
S1 = s2 = s3
В то же время мы избежим и создания ложных объектов в виде копий для
возврата результата, как было, когда этот возврат осуществлялся по значению.
Ниже представлен листинг программы STRIMEM2.
Листинг 11.22. Программа STRIMEM2
// strimem2.cpp
// Класс String с экономией памяти
// Перегружаемая операция присваивания и указатель this
#include <iostream>
#include <cstring> //для strcpy() и т. д.
using namespace std;
///////////////////////////////////////////////////////////
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 //класс String
{
private:
strCount* psc; //указатель на strCount
public:
String() //конструктор без аргументов
{ psc = new strCount("NULL"); }
//---------------------------------------------------------
String(char* s) //конструктор с одним аргументом
{ psc = new strCount(s); }
//---------------------------------------------------------
String(String& S) //конструктор копирования
{
cout << "\nКОНСТРУКТОР КОПИРОВАНИЯ";
psc = S.psc;
(psc->count)++;
}
//---------------------------------------------------------
~String() //деструктор
{
if(psc->count==1) //если последний //пользователь,
delete psc; //удалить strCount
else //иначе
(psc->count)--;//уменьшить счетчик
}
//---------------------------------------------------------
void display() //вывод String
{
cout << psc->str; //вывести строку
cout << " (addr=" << psc << ")"; //вывести адрес
}
//---------------------------------------------------------
String& operator = (String& S)//присвоение String
{
cout << "\nПРИСВАИВАНИЕ";
if(psc->count==1) //если последний //пользователь,
delete psc; //удалить strCount
else //иначе
(psc->count)--;// уменьшить счетчик
psc = S.psc; //использовать strCount //аргумента
(psc->count)++; //увеличить счетчик
return *this; //вернуть этот объект
}
};
///////////////////////////////////////////////////////////
int main()
{
String s3 = "Муха по полю пошла, муха денежку нашла";
cout << "\ns3="; s3.display(); //вывести s3
String s1,s2; //определить объекты String
s1 = s2 = s3; //присваивания
cout << "\ns1="; s1.display(); //вывести их
cout << "\ns2="; s2.display();
cout << endl; //ожидать нажатия клавиши
return 0;
}
Теперь описателем оператора присваивания является
String& operator = (String& S)//возврат по ссылке
и, как в программе ASSIGN2, функция возвращает указатель на this. Результат ра-
боты программы:
s3=Myxa по полю пошла, муха денежку нашла (addr=0x8f640d3a)
ПРИСВАИВАНИЕ
ПРИСВАИВАНИЕ
s2=Myxa по полю пошла, муха денежку нашла (addr=0x8f640d3a)
s1=Myxa по полю пошла, муха денежку нашла (addr=0x8f640d3a)
Результат работы программы показывает, что в соответствии с выражением
присваивания все три объекта класса String указывают на один и тот же объект
класса strCount.
Следует отметить, что указатель this нельзя использовать в статических ме-
тодах, так как они не ассоциированы с конкретным объектом.
Остерегайтесь неправильных присваиваний
Один из основополагающих так называемых «законов Мёрфи» говорит о том,
что все, что можно случайно сделать не так, обязательно будет кем-нибудь сдела-
но. Это абсолютная правда в отношении программирования, поэтому будьте уве-
рены: если вы переопределили оператор присваивания, кто-нибудь станет его ис-
пользовать для присваивания объектов самим себе. Вот так:
alpha = alpha;
Перегружаемый = должен быть морально готов обработать такую ситуацию.
В противном случае не ждите ничего хорошего. Например, в секции main() про-
граммы STRIMEM2, если вы положите объект String равным самому себе, програм-
ма зависнет (несмотря на то что другие объекты без проблем используют один и
тот же объект strCount). Проблема заключается в том, что согласно коду пере-
гружаемой операции присваивания последний удаляет объект strCount, если счи-
тает, что вызываемый объект является единственным, использующим strCount.
Присваивание объекта самому себе убедит оператор в этом прискорбном факте,
хотя на самом деле никто и не собирался удалять никакие объекты.
Чтобы привести все в порядок, нужно ввести проверку на присваивание само-
му себе в начале работы каждого перегружаемого =. В большинстве случаев это
делается путем сравнения адресов объектов, стоящих справа и слева от знака
равенства. Понятно, что если адреса равны, значит, осуществляется присваива-
ние объекта самому себе и необходимо немедленно вернуться из этой функции.
(Если кто-то не понял, в чем проблема, поясняем, что не нужно присваивать
объекты самим себе — они и так уже равны.) Например, в STRIMEM2 с этой це-
лью можно вставить следующие две строчки
if(this == &S)
return *this;
в начало функции operator=(). Это должно решить возникшую проблему.
12 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
Схожі підручники
- ТЕХНОЛОГІЇ ХІМІЧНИХ ВИРОБНИЦТВ ТА НАФТОПЕРЕРОБЛЕННЯ
- Прежде чем начать СВОЙ БИЗНЕС (онлайн)
- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 7) онлайн
- ТЕМА 12. ТЕОРЕТИЧНІ ЗАСАДИ ПРОЦЕНТА
- РЕАЛЬНІ ІНВЕСТИЦІЇ
- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 3) онлайн
