|
Программирование >> Синтаксис инициирования исключений
Здесь оператор -> вызывается трижды: 1. Pointer2::operator-> возвращает Pointer. 2. Затем Pointer::operator-> возвращает Foo. 3. Компилятор интерпретирует Foo* как базовый тип и вызывает его функцию. В отличие от всех остальных операторов, вы не можете контролировать возвращаемое значение или просто обратиться к нему после выхода из операторной функции. Оно используется исключительно самим компилятором. Концепция объекта, переодетого указателем, имеет фундаментальное значение для всей книги. Оператор [] Оператор [] может быть перегружен, чтобы получать единственный аргумент произвольного типа и возвращать произвольный тип в качестве своего значения. class String { private: char* s; public: String(char*); char operator[](int n) const; n-й символ char String::operator[](int n) Здесь должна выполняться проверка диапазона return s[n]; Поскольку оператор [] может вызываться лишь с одним аргументом, для имитации многомерных массивов часто применяют анонимные экземпляры. struct Index3 { int X, У, Z; Index3(int x, int y, int z) : X(x), y(y), Z(z) {} class Array3D { Трехмерный массив объектов String private: Настоящая структура данных public: String& operator[](const Index3&); String s = anArray[lndex3(17, 29, 31)]; Хотя оператор [] вызывается лишь с одним аргументом, этот умеренно неуклюжий синтаксис позволяет создать произвольное количество псевдоаргументов. Оператор () Наверное, вы и не подозревали, что этот оператор тоже можно перегрузить. Если у вас часто возникает непреодолимое желание превратить объект в функцию, возможно, ваша психика нестабильна и вам стоит серьезно подумать над сменой рода занятий. Тем не менее, C++ с пониманием отнесется к вашим проблемам. Левостороннее выражение оператора () представляет собой объект, который должен интерпретироваться как вызываемая функция. Аргументы оператора представляют собой формальные аргументы, передаваемые объекту-функции при вызове. class Function { public: int operator()(char*); int Function::operator()(char* s) cout << \ << s << \ ; Function fn; int x = fn( He11o ); Выводит в cout строку Hello Оператор () может возвращать любой тип и принимать любые аргументы. При этом он подчиняется тем же правилам, что и любая другая функция. Оператор () перегружается только в форме функции класса. Оператор new Изобретая новый язык, приходится изобретать собственные правила. Одно из новых правил C++ гласит, что имя оператора не обязано состоять из одних служебных символов. Исключение составляют операторы new и delete. Оператор new вызывается всякий раз, когда компилятор считает, что настало время выделить из кучи память для нового объекта. Используемый здесь термин объект относится к любым динамическим данным, включая int, char* и т.д., а не только к экземплярам ваших классов. Оператор new по умолчанию имеет следующий интерфейс: void* operator new(size t bytes); Единственный аргумент - количество выделяемых байт, возвращаемое значение - адрес выделенной области. Оператор new никогда не должен возвращать NULL; вместо этого при нехватке памяти инициируется исключение (см. главу 4). Реализация оператора new по умолчанию изменяется от простого вызова функции mall ос или call ос до нестандартных средств управления памятью, прилагаемых к компилятору. Оператор new может перегружаться как в форме внешней функции, так и в форме функции класса. При перегрузке в форме функции класса он наследуется, поэтому выделение памяти в производных классах будет осуществляться так же, как и в базовом классе, перегрузившем оператор new. class Foo { public: void* operator new(size t bytes); void* Foo::operator new(size t bytes) if (bytes < MAXBYTES) Нестандартное выделение памяти для блоков малого размера else return ::operator new(bytes); Разумеется, всеобщая перегрузка глобального оператора new затруднит создание переносимого кода, поэтому обычно предпочтительным вариантом является перегрузка в форме функции класса. Она имеет дополнительные преимущества: из перегруженной функции можно вызвать оператор new по умолчанию, как это делается в предыдущем фрагменте. Оператор new также может перегружаться для других сигнатур. Хотя дополнительные сигнатуры не будут вызываться компилятором автоматически, они оказывают большую помощь при реализации нестандартного управления памятью, в которой можно вызвать конкретную версию оператора new. class Pool { Нестандартный пул памяти public: virtual void* Al1ocate(size t bytes); void* operator new(size t bytes, Pool* p) return p->Al1ocate(bytes); extern Pool* DefaultPool; Foo* f = new(DefaultPool) Foo; Дополнительные аргументы указываются между ключевым словом new и перед именем класса Foo; они передаются перегруженному оператору после размера блока, предоставленного компилятором. Оператор delete Оператор delete обычно перегружается вместе с оператором new для выполнения нестандартных операций управления памятью. Существуют два интерфейса, автоматически вызываемые компилятором при удалении объекта: 1. void operator de1ete(void* address); 2. void operator de1ete(void* address, size t bytes); Первая версия просто передает вам адрес освобождаемого блока; чтобы определить его размер, вам придется самостоятельно заглянуть в хрустальный шар. Вторая версия вроде бы передает размер освобождаемого блока, но... он может быть меньше истинного размера! Проблемы возникают в следующих ситуациях: class Foo { private: int x; public: ~Foo(); Невиртуальный деструктор class Bar : public Foo { private: int y; public: ~Bar(); Bar* b = new Bar; delete b; Правильный размер Foo* f = new Bar; delete f; Размер Foo, а не Bar Компилятор определяет размер на основании вызываемого деструктора. Если невиртуальный деструктор вызывается для указателя на базовый класс, используется размер базового класса. Правильный размер будет передаваться при соблюдении любого из трех условий: 1 . Деструктор является виртуальным. 2. Указатель ссылается на настоящий тип объекта. 3. Тип, на который ссылается указатель, имеет тот же размер, что и настоящий тип. Последнее условие соблюдается лишь в том случае, если в производном классе не добавляется ни одной новой переменной, а базовый и производный классы одновременно либо содержат, либо не содержат виртуальных функций (и как следствие, указателей v-таблицы). Непонятно? Тогда запомните простое правило - объявляйте ваши деструкторы виртуальными. Оператор delete, как и оператор new, можно перегружать как в форме функции класса, так и в форме внешней функции. Если оператор перегружается функцией класса, он наследуется. В отличие от
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |