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

1 ... 10 11 12 [ 13 ] 14 15 16 ... 96



в этом примере в заключительной части заголовка цикла i увеличивается, а j уменьшается на 1. Здесь удобно использовать оператор-запятую, потому что в заключительной части заголовка можно поставить только одно выражение; отдельные операторы, меняющие значения i и j, здесь были бы недопустимы.

Оператор-запятая подчиняется правилам, аналогичным тем, которые в языке С++ описывают поведение операторов && и I I для встроенных типов. В выражении, содержащем запятую, сначала оценивается часть выражения слева от запятой, затем - справа. За значение всего выражения принимается значение части, стоящей справа от запятой. Таким образом, в выражении в заключительной части цикла for сначала оценивается ++i, затем --j, а результат, возвращаемый как значение всего выражения, равен -j.

Если вы захотите написать свой собственный оператор-запятую, вам придется скопировать его поведение, описанное выше. К сожалению, это невозможно.

Если operator, не принадлежит к какому-либо классу, вы не пол5Д1Ите гарантии, что левое выражение будет оценено раньше, чем правое, потому что оба выражения будут переданы функции operator, в качестве аргументов, а порядок вычисления аргументов при вызове функции контролировать нельзя. Поэтому внеклассовый подход исключен.

У вас остается единственная возможность - написать operator, как член класса. Но даже здесь нельзя быть твердо уверенным, что левый по отношению к запятой операнд будет вычислен сначала, ведь компиляторы не обязаны обеспечивать такой порядок. Следовательно, вы не можете перегрузить оператор-запя- тую и гарантировать его надлежащее поведение. Поэтому перегружать его было бы неразумно.

Наверное, вы интересуетесь, придет ли конец этому перегружаемому безумию. В конце концов, если можно перегрузить даже оператор-запятую, неужели существует что-то, чего перегружать нельзя? Оказывается, да. Нельзя перегружать следующие операторы:

. * : : ? :

new delete sizeof typeid

static cast dynamic cast const cast reinterpret cast

A вот эти операторы перегружать можно:

operator new operator delete

operator new[] operator delete[] + -*/% & I

!=<>+=-=*= /= %= &= 1= = = == ! = <=>=&&!!++- , ->* ->

0 []



(Об операторах new и delete, а также об operator new, operator delete, operator new [ ] и operator delete [ ] см. правило 8).

Конечно, сама по себе возможность переопределить операторы является недостаточной причиной, чтобы заняться этим немедленно. Цель перегрузки операторов состоит в том, чтобы программы было проще писать, читать и понимать, а не поражать других знанием оператора-запятой. Не следует перегружать оператор, если у вас нет для этого серьезной причины. В случае же операторов &&, и , достойный повод вообще трудно найти, потому что, как бы вы ни старались, заставить эти операторы вести себя, как положено, вам не удастся.

Правило 8. Различайте значение операторов new и delete

Иногда кажется, что люди специально стараются затруднить понимание терминов языка C-I-I-. Примером тому может служить разница между операторами new и operator new.

В коде:

string *ps = new string { Memory Management ) ;

используется оператор new. Этот оператор является встроенным, так же как, например, sizeof, и его действие нельзя изменить. Он выполняет две функции. Во-первых, выделяет память, достаточную для размещения объекта указанного типа. В вышеприведенном примере это объект string. Во-вторых, вызывает конструктор для инициализации объекта в выделенной памяти. Оператор new всегда выполняет обе названные функции, и его поведение изменить нельзя.

Зато можно изменить способ, которым выделяется память для объекта. Для выделения памяти оператор new вызывает специальную функцию и, чтобы изменить поведение оператора, эту функцию допускается переписать или перегрузить. Имя функции - operator new.

Функция operator new обычно объявляется следующим образом:

void* operator new(size t size) ;

Тип возвращаемого значения - void*, потому что функция возвращает указатель на неинициализированную память. (По желанию можно написать другую версию функции operator new, записывающую в выделяемую память какое-либо значение, но обычно никто этого не делает.) Аргумент типа size t указывает размер памяти, которую нужно выделить. Можно перегрузить функцию operator new, добавив дополнительные аргументы, но тип первого аргумента всегда должен быть равен size t.

У программистов очень редко возникает необходимость вызывать функцию operator new непосредственно, но когда это приходится делать, она вызывается, как любая другая функция:

void*rawMemory = operatornew{sizeof (string) ) ;

В этом примере функция operator new вернет указатель на область памяти, достаточно большую для хранения объекта string.



Единственная задача функции operator new, так же как и оператора malloc, состоит в выделении памяти. О конструкторах она ничего не знает. Оператор new получит неинициализированную память, выделенную функцией operator new, и преобразует ее в объект. При обработке строки программы типа:

string *ps = new string ( Memory Management ) ;

компиляторы создадут код, похожий на:

void *memory = Получить

operator new (sizeof{string)); неинициализированную

/ / память для объекта string.

call string::string{ MemoryManagement )

on *memory; Инициализировать объект

/ / в памяти.

string*ps= Создатьуказатель

static cast<string*>(memory); на новый объект.

Заметьте, что на втором шаге вызывается конструктор. Использовать конструктор для инициализации уже выделенной памяти (вгслючая такой ключевой элемент, как таблица виртуальных функций объекта - см. правило 24) программисту запрещено. В этом смысле компиляторы выше простых смертных, они могут делать, что захотят. Программист же должен использовать для создания динамического объекта оператор new.

Буферизованный оператор new

Иногда необходгшо вызвать конструктор непосредственно. Вызов конструктора для уже существующего объекта особого смысла не имеет, потому что инициализировать объект по начальным данным можно только один раз. Но иногда уже есть область выделенной памяти, и требуется создать в ней объект. Для этого служит специальная форма функции operator new, которая называется буферизованным оператором new.

В качестве примера использования данного оператора рассмотрим следующий код:

class Widget{ public:

Widget(intwidgetsize) ;

Widget * constructWidgetlnBuf fer {void*buf f er, Intwidgetsize)

return new (buffer) Widget (widgetSize) ;

Эта функция возвращает указатель на объект Widget, который создается в буфере, передаваемом функции в качестве аргумента. Данная функция может



1 ... 10 11 12 [ 13 ] 14 15 16 ... 96

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