|
Программирование >> Оптимизация возвращаемого значения
Moro FIfcUiVP Рис. 5.10 Создаваемые по отдельности объекты String с одинаковым начальным значением не будут использовать структуру данных совместно, поэтому такой код: string si{ More Effective С++ ); String s2{ More Effective С+ + ) ; дает структуру данных, изображенную на рис. 5.11. - ►О-0- Mote Effective С+- More Effective С Рис. 5.11 Избежать такого дублирования можно, отслеживая в объекте String (или StringValue) существующие объекты StringValue и создавая новые объекты только для уникальных строк, но такое усовершенствование подсчета ссылок находится в стороне от основного пути. Поэтому я оставлю его в качестве страшного и ненавистного упражнения для читателей. Конструктор копирования класса string совсем не сложен и очень эффективен: вновь созданный объект String совместно использует тот же самый объект StringValue, что и копируемый объект String. string: : string (const StringSc rhs) : value(rhs.value) { ++value->refCount; Следующий код: string si( More Effective С++ ); String s2 = si; дает в результате структуру данных, показанную на рис. 5.12. More Вloctive 0+-- Рис. 5.12 Такой класс намного эффективнее, чем обычная (без подсчета ссылок) реализация класса string, поскольку при этом не нужно выделять память для второй копии значения строки, нет необходимости впоследствии освобождать память, и не требуется копировать значение, которое будет помещено в эту память. Вместо этого достаточно скопировать указатель и увеличить значение счетчика ссылок на единицу. Реализация деструктора String тоже довольно проста, так как большую часть времени он ничего не делает. Пока счетчик ссылок на объект StringValue не равен нулю, по меньшей мере один объект String использует данное значение, поэтому оно не должно быть уничтожено. Только когда уничтожаемый объект String является единственным объектом, использующим данное значение, то есть когда счетчик ссылок на него равен 1, деструктор string должен уничтожать объект StringValue: class String { public: -String{); String::-String() { if {--value->refCount == 0) delete value; Сравните эффективность этой функции с деструктором в реализации без счетчика ссылок. Такая функция всегда вызывала бы оператор delete, и ее выполнение почти всегда требовало бы значительных затрат. Если у различных объектов string вдруг окажутся одинаковые значения, то приведенная реализация будет просто уменьшать значение счетчика на единицу и проверять его на равенство нулю. Это все, что относится к созданию и уничтожению объектов String, поэтому перейдем к оператору присваивания класса string: class String { public: Strings operator=(const Strings rhs); Когда пользователь пишет такой код: si = s2; И si, и s2 - объекты типа String. то в результате присваивания si и s2 должны указывать на один и тот же объект StringValue. Поэтому во время присваивания значение счетчика ссылок должно быть увеличено на единицу. Кроме того, значение счетчика ссылок на объект StringValue, на который указывал объект si до присваивания, должно уменьшиться на единицу, поскольку s 1 уже не имеет это значение. Если объект s 1 был единственным объектом типа String с данным значением, то оно должно быть уничтожено. В языке С++ это выглядит примерно так: strings String::operator=(const Strings rhs) { if (value == rhs.value) { , Если значения уже return *this; одинаковы, ничего не } делать; обычная проверка для значения Srhs. if {--value->refCount == 0) { delete value; value = rhs. value; ++value->refCount; return *this; Уничтожить значение *this, если его больше никто не использует. Объекты *this и rhs использует одно значение. Копирование при записи Завершая изучение строк со счетчиком ссылок, рассмотрим оператор [ ], по-зволяюший считывать и записывать отдельные символы в строке: class String { public: const charSc operator[](int index) const; Для const String. charSc operator [] (int index) ; Для не-const объектов String. Реализовать const версию функции несложно, так это просто операция чтения; значение строки не может изменяться: const charSc String::operator[](int index) const { return value->data[index]; (B этой функции проверка правильности index выполняется в соответствии с главной традицией С++, то есть не выполняется вообще. Если вам нужна проверка корректности параметра, вы можете легко добавить ее сами.) He-const версия operator [ ] - совсем другая история. Эта функция может вызываться как для считывания символа, так и для его записи: string S; cout << s[3] s[5] = X ; Это чтение. Это запись. Конечно, хотелось бы по-разному обрабатывать чтение и запись. Простое чтение можно выполнять точно так же, как и для вышеприведенной const версии operator [ ], но запись должна быть реализована иначе. Когда вы изменяете значение объекта String, будьте внимательны, чтобы не изменить значение других объектов String, совместно использующих тот же самый объект StringValue. К несчастью, компиляторы С++ не могут сообщить вам, запись или чтение осуществляется посредством функции operator [ ], поэтому
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |