|
Программирование >> Оптимизация возвращаемого значения
{ counter->removeReference{) ; } template<class T> RCIPtr<T>& RCIPtr<T>::operator={const RCIPtrb rhs) { if (counter != rhs.counter) { counter->removeReference{); counter = rhs.counter; initO ; return *this; template<class T> void RCIPtr<T>::makeCopY() { if (counter->isShared{)) { T *oldValue = counter->pointee; counter->removeReference() ; counter = new CountHolder; counter->pointee = new T(*oldValue) counter->addReference(); template<class T> const T* RCIPtr<T>::operator->{) const { return counter->pointee; } template<class T> T* RCIPtr<T>::operator->{) { makeCopy(); return counter->pointee; } template<class T> const T& RCIPtr<T>::operator*() const { return *(counter->pointee); } template<class T> TSc RCIPtr<T>: : operator* () { makeCopyO; return * {counter->pointee) ; Реализация копирования для копирования при записи. Доступ к const; копирование при записи не нужно. Доступ к He-const; нужно копирование при записи. Доступ к const; копирование при записи не нужно. Доступ к He-const; нужно копирование } при записи. Класс RCIPtr имеет только два отличия от класса RCPtr. Во-первых, объекты RCPtr непосредственно указывают на значения, а объекты RCIPtr указывают на значения через промежуточные объекты CountHolder. Во-вторых, класс RCIPtr перегружает функции operator-> и operator*, поэтому в случае He-const доступа к указываемому объекту копирование при записи происходит автоматически. Если имеется класс RCIPtr, достаточно просто реализовать класс RCWidget, поскольку каждая функция в классе RCWidget реализуется с помощью передачи вызова через лежащий ниже класс RCIPtr объекту Widget. Например, если класс Widget выглядит так: class Widget { public: Widget(int size); Widget(const Widget& rhs); -Widget (); WidgetSc operator= (const Widget& rhs) ; void doThis () ; int showThatO const; TO класс RCWidget будет определяться следующим образом: class RCWidget { public: RCWidget(int size) : value(new Widget(size)) {} voiddoThisO { value->doThis () ; } int showThat() const ( return value->showThat(); } private: RCIPtr<Widget> value; Обратите внимание: конструктор RCWidget вызывает конструктор Widget (при помощи оператора new - см. правило 8) с переданным ему аргументом; функция doThi S класса RCVli dget вызывает функцию doThi s в классе Widget; а функция RCWidget::showThat возвращает то же самое, что и ее двойник в классе Widget. Обратите также внимание на то, что в классе RCWidget не объявлены конструктор копирования, оператор присваивания и деструктор. Как и в случае класса string нет необходимости писать эти функции. Благодаря поведению класса RCIPtr версии, установленные по умолчанию, работают правильно. Если вам кажется, что создание класса RCWidget настолько предопределено, что его можно автоматизировать, то вы не ошиблись. Было бы несложно написать программу, на вход которой подавался бы класс, подобный Widget, а на выходе получался бы аналог класса RCWidget. Если вы напишете такую программу, пожалуйста, сообщите мне об этом. Оценка Давайте теперь попробуем выпутаться из деталей строк, значений, интеллектуальных указателей и базовых классов для подсчета ссылок. Для этого взглянем на процесс подсчета ссылок как бы со стороны и попытаемся решить вопрос более высокого уровня; является ли подсчет ссылок подходящим методом? Реализация подсчета ссылок имеет свою цену. Каждое значение содержит счетчик ссылок, и большинство операций требует проверки или изменения этого счетчика. Поэтому для значений объектов необходимо больше памяти, и иногда при работе с ними выполняется больше кода. Кроме этого, лежащий в основе метода исходный код намного сложнее для класса с подсчетом ссылок, чем для менее трудоемкой реализации. Класс строк с подсчетом ссылок обычно ни от чего не зависит, а последняя версия нашего класса string бесполезна, если она не основана на трех вспомогательных классах (StringValue, RCObject и RCPtr). Но сложная схема обещает большую эффективность за счет того, что значения могут использоваться совместно, устраняется необходимость отслеживать владельца объекта и стимулируется повторное применение кода для подсчета ссылок. Однако все четыре класса нужно написать, протестировать, документировать и поддерживать, что, конечно, потребует больше работы, чем соответствующие операции для одного класса. Это способен понять даже менеджер. Подсчет ссылок - метод оптимизации, основанный на предположении, что объекты обычно будут совместно использовать значения (см. правило 18). Если же это предположение не подтверждается, то подсчет ссылок потребует больше памяти, чем обычная реализация, и при этом будет выполняться больше кода. С другой стороны, если объекты часто имеют общие значения, подсчет ссылок позволит сэкономить время и память. Чем больше значения объектов и чем больше объектов используют значения совместно, тем больше памяти вы сэкономите. Чем чаще выполняется копирование и присваивание значений объектов, тем значительнее будет экономия времени. Чем больших затрат требует создание и уничтожение значения, тем больше вы сэкономите времени. Короче говоря, подсчет ссылок позволяет повысить эффективность программы при следующих условиях: □ немного значений совместно используется большим числом объектов. Такое совместное использование обычно возникает при вызовах операторов присваивания и конструкторов копирования. Чем больше отношение число объектов/число значений , тем лучше этот случай подходит для подсчета ссылок; □ создание и уничтожение значений объектов требует больших затрат, или они используют много памяти. Даже если это так, подсчет ссылок ничего вам не дает, если значения не могут использоваться совместно несколькими объектами. Есть только один надежный способ выяснить, удовлетворяются ли эти условия. Не стоит гадать или полагаться на программистскую интуицию (см. правило 16), лучше сразу использовать отладчик, чтобы определить, выиграет ли программа от применения подсчета ссылок. При этом вы можете определить, является ли создание и разрушение значений узким местом производительности, и измерить отношение число объектов/число значений . Только такие данные позволят вам выяснить, перевешивают ли преимущества подсчета ссылок (которых множество) недостатки этого метода (которых тоже предостаточно). Даже если удовлетворяются вышеприведенные условия, подсчет ссылок может все же оказаться неподходящим методом. Некоторые структуры данных (например, неориентированные графы) порождают структуры, ссылающиеся сами на себя или с кольцевой зависимостью. В таких структурах данных возникают изолированные наборы объектов, которые никто не использует, и счетчики ссылок на которые никогда не становятся равными нулю. Это связано с тем, что на каждый объект в неиспользуемой структуре указывает по меньшей мере один другой объект в той же структуре. Коммерческие схемы для сборки мусора применяют специальные методы для поиска и устранения таких структур, но простой прием подсчета ссылок, который рассматривался выше, нелегко расширить, чтобы включить в него эти методы.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |