Программирование >>  Оптимизация возвращаемого значения 

1 ... 62 63 64 [ 65 ] 66 67 68 ... 96


String::StringValue::StringValue(const StringValueSc rhs) {

data = new char[strlen{rhs.data) + 1] ; strcpy{data, rhs.data);

Существование конструктора детального копирования не единственное требование, которое интеллектуальный указатель RCPtr<T> предъявляет к классу Т. Кроме того, класс Т должен наследовать от класса RCObj ect или обеспечивать ту же функциональность. Это достаточно разумно, если принять во внимание, что объекты RCObj ect были разработаны для того, чтобы указывать только на объекты со счетчиком ссылок. Но несмотря на его очевидность, данное требование следует документировать.

И наконец, предполагается, что RCPtr<T> указывает на объект типа т. Это также кажется достаточно очевидным. В конце концов, тип pointee объявляется как Т*. Но в действительности pointee может указывать на производный от Т класс.

Проблема заключается в следующем. Когда функция init должна создать новую копию значения (так как существующая копия не может использоваться совместно), она выполняет следующий код:

pointee = new T{*pointee);

Объект pointee имеет тип указателя на Т, поэтому этот оператор создает новый объект Т и инициализирует его, вызывая конструктор копирования т. Для указателя RCPtr В классе String объектом Т будет String: : StringValue, поэтому приведенный оператор вызовет конструктор копирования для String: :String-Value. Но вы не объявили для этого класса конструктор копирования, поэтому он будет создан компилятором. Полученный конструктор копирования будет, в соответствии с правилами для автоматически создаваемых конструкторов копирования языка С++, копировать только указатель data объекта StringValue, он не будет копировать строку char*, на которую ссылается указатель data. Такое поведение - бедствие почти для любого класса (а не только для класса с подсчетом ссылок), и поэтому вам следует создавать конструктор копирования (и оператор присваивания) для всех классов, содержащих указатели.

Корректность поведения шаблона RCPtr<T> зависит от того, содержит ли класс Т конструктор копирования, создающий полностью независимую копию (то есть выполняющий детальное копирование) значения, представляемого этим объектом. Вы должны добавить такой конструктор в класс StringValue прежде, чем использовать его в классе RCPtr:

class String { private:

struct StringValue: public RCObject { StringValue(const StringValue& rhs);



Например, если бы имелся класс SpecialStringValue, наследующий от класса String::StringValue:

class String { private:

struct StringValue: public RCObject { ... } ;

struct SpecialStringValue: public StringValue { ... };

TO в итоге мог бы пол5иться класс string, в котором RCPtr<StringValue> указывал бы на объект SpecialStringValue. В таком случае хотелось бы, чтобы эта часть функции init:

pointee = new T(*pointee); Т имеет тип StringValue,

но pointee указывает на тип SpecialStringValue.

вызывала конструктор копирования класса SpecialStringValue, а не String-Value. Это можно сделать при помощи виртуального конструктора копирования (см. правило 25). В сл5ае рассматриваемого класса String не предполагается, что какие-либо классы будут наследовать от класса StringValue, поэтому этим вопросом можно пренебречь.

После того как вы разобрались с конструкторами класса RCPtr, с остальными функциями класса справитесь намного быстрее. Присваивание для объектов RCPtr выполняется достаточно просто, нужно только проверить, можно ли использовать вновь присвоенное значение совместно. К счастью, такая проверка уже выполняется функцией init, которая была создана для конструкторов RCPtr. Снова используем ее здесь:

template<class Т>

RCPtr<T>& RCPtr<T>::operator=(const RCPtrSc rhs) {

if (pointee != rhs.pointee) { He выполнять

присваивание, если значение не меняется.

if (pointee) {

pointee->removeReferenceО; Удалить

} ссылку на текущее

значение.

pointee = rhs.pointee; Указывает на новое

initО; значение, если возможно

} использовать его

совместно, иначе создать

return *this; собственную копию.

Деструктор устроен проще. При уничтожении объекта RCPtr он просто удаляет его ссылку на объект, для которого выполняется подсчет ссылок:



template<class Т> RCPtr<T>::-RCPtr{) {

if (pointee)pointee->removeReference{) ;

Если этот интеллектуальный указатель RCPtr был последней ссылкой на объект, то будет уничтожен объект внутри функции removeRef erence - члена класса RCObject. Следовательно, объекты RCPtr не должны беспокоиться об уничтожении значений, на которые они указывают.

И наконец, операторы в классе RCPtr, эмулирующие указатели, являются частью стандартной библиотеки интеллектуальных указателей, о которой вы можете прочитать в правиле 28:

template<class Т>

Т* RCPtr<T>::operator->{) const { return pointee; } template<class T>

T& RCPtr<T>::operator*{) const { return *pointee; }

Резюме

Теперь можно свести все части вместе и построить класс string с подсчетом ссылок, основанный на классах RCObject и RCPtr. Надеюсь, вы не забыли, что это и было исходной целью упражнения.

Каждая строка с подсчетом ссылок реализуется при помощи структуры данных, схема которой представлена на рис. 5.15.


класс

RCObject

Открытое

наследование

ОбЬСК)

Указатель

Куча

Рис. 5.15



1 ... 62 63 64 [ 65 ] 66 67 68 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика