51
Здесь оператор
->
вызывается трижды:
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, Y, 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[Index3(17, 29, 31)];
Хотя оператор
[]
вызывается лишь с одним аргументом, этот умеренно неуклюжий синтаксис
позволяет создать произвольное количество псевдоаргументов.
Оператор ()
Наверное, вы и не подозревали, что этот оператор тоже можно перегрузить. Если у вас часто возникает
непреодолимое желание превратить объект в функцию, возможно, ваша психика нестабильна и вам
стоит серьезно подумать над сменой рода занятий. Тем не менее, C++ с пониманием отнесется к вашим
проблемам. Левостороннее выражение оператора
()
представляет собой объект, который должен
интерпретироваться как вызываемая функция. Аргументы оператора представляют собой формальные
аргументы, передаваемые объекту-функции при вызове.
52
class Function {
public:
int
operator()(char*);
};
int Function::operator()(char* s)
{
cout << “\”” << s << “\””;
}
Function fn;
int x = fn(“Hello”);
// Выводит в 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* Allocate(size_t bytes);
Do'stlaringiz bilan baham: |