Программирование >>  Синтаксис инициирования исключений 

1 ... 8 9 10 [ 11 ] 12 13 14 ... 82


public:

Foo& operator=(const Foo&);

class Bar { public:

Оператор = не перегружен

class FooBar { private:

Foo f;

Bar b; public:

FooBar& operator=(const FooBar&);

FooBar& FooBar::operator=(const FooBar& fb)

if (this == &fb) return *this;

f = fb.f; Используется перегруженный оператор = класса Foo

f = fb.b; Используется оператор = по умолчанию

return *this;

Применяя эту методику, вы не заботитесь о том, существует ли для переменной перегруженный оператор =. Об этом должен думать компилятор.

Присваивание для базовых классов

Присваивание для базовых классов сопряжено с некоторыми синтаксическими ухищрениями. Если вы никогда их не видели, вероятно, на поиск правильной комбинации уйдет немало времени. Выглядит она так:

class Foo {...}

class Bar : public Foo {

public:

Bar& operator=(const Bar&);

Bar& Bar::operator=(const Bar& b)

if (this == &b) return *this; this->Foo::operator=(b); Чего-чего?

return *this;

Другие варианты, которые могут придти в голову (например, *((Foo)this)=b; ), не работают - поверьте мне на слово. Все они создают временные копии. Показанный вариант работает, поскольку компилятор знает, как преобразовать Bar в Foo в аргументе. Он работает независимо от того, перегружали вы Foo::operator= или нет. Даже если не перегружали, оператор все равно присутствует, и его можно вызвать по полному имени Foo::operator=.

Другие сигнатуры оператора =

Оператор = не ограничен одной сигнатурой. Его можно перегрузить так, чтобы в правой части присваивания мог стоять аргумент любого другого типа. Сигнатура Х& X::operator=(const Х&) выделяется на общем фоне тем, что компилятор предоставляет ее версию по умолчанию и использует эту сигнатуру в стандартном алгоритме рекурсивного присваивания.



class String { Как раньше public:

String& operator=(const String&); Нормальный вариант String& operator=(char*); Перегруженный вариант

String& operator=(int); Вызывает atoi()

В показанном фрагменте создается несколько перегруженных вариантов оператора = для различных типов данных в правой части выражения. Второй вариант позволяет избежать конструирования временного объекта String из char* лишь для того, чтобы присвоить его объекту в левой части. Третий вариант выполняет преобразование другого рода. Тем не менее, лишь первый вариант перегружает (то есть заменяет) версию оператора по умолчанию.

Перегрузка операторов

Одна из приятных особенностей C++ - возможность расширения смысла операторов. Это упрощает чтение программы, поскольку вам уже не придется изобретать дурацкие имена функций вроде Add там, где знак + имеет совершенно очевидный смысл. Тем не менее, из личного опыта я знаю две проблемы, связанные с перегруженными операторами. Во-первых, их чрезмерное применение превращает программу в хаос. Во-вторых, большинство программистов никогда их не использует. Приведенный ниже список не претендует на полноту, однако он поможет подготовить поле для дальнейшего изложения материала.

Функциональная форма операторов

Операторы (например, +) используются в двух вариантах: как особая синтаксическая форма или как функция. В C++ функциональная форма всегда представляет собой ключевое слово operator , за которым следует символ оператора.

class Foo {...} Foo x, y, z;

z = x + y; Инфиксная (нормальная) форма

z = operator+(x, y); Функциональная форма (внешняя функция)

z = x.operator+(y); Функциональная форма (функция класса)

С концептуальной точки зрения три последние строки эквивалентны, хотя на практике, вероятно, оператор будет определен либо в виде внешней функции, либо в виде функции класса, но не в обоих вариантах сразу. Для бинарных операторов знак оператора указывается между двух аргументов в инфиксной форме. В форме внешней функции оба аргумента передаются глобальной функции. В форме функции класса объект, которому принадлежит вызываемый оператор, указывается слева, а аргумент - справа от знака оператора. Унарные операторы (такие как ! и ~) тоже могут перегружаться. Форма внешней функции вызывается с одним аргументом, а форма функции класса вызывается без аргументов (операция выполняется с объектом, находящимся слева от оператора . или

->).

Не разрешается перегружать встроенные операторы (например, оператор целочисленного сложения). Чтобы обеспечить выполнение этого условия, компилятор требует, чтобы хотя бы один аргумент каждого перегруженного оператора относился к пользовательскому типу (обычно к классу). Выбор ограничен операторами, уже определенными в C++. Во время долгих ночных отладок мне часто хотелось создать оператор с именем #$%а&, но C++ на этот счет неумолим.

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



Перегрузка операторов в форме внешних функций

Чтобы перегрузить оператор в форме внешней функции, необходимо определить глобальную функцию.

class String {

friend String& operator+(const String&, const String&); private:

char* s; public:

Конструкторы и т.д.

String& operator+(const String& s1, const String& s2)

char* s = new char[str1en(s1.s) + str1en(s2.s) + 1]; strcat(s, s1.s, s2.s); String newStr(s); delete s; return newStr;

String s1 = Hello ; String s2 = Goodbye ; String s3 = s1 + s2;

Перегруженная функция выглядит так же, как и любая глобальная функция (если не считать странного имени). Именно для таких случаев и были придуманы друзья. Если бы мы не объявили функцию operator+ другом, то она не имела бы доступа к переменной s, и мы оказались бы перед выбором: то ли разрешить всем на свете доступ к char*, то ли перейти к менее эффективной реализации, при которой строка копируется при каждом обращении к ней. С концептуальной точки зрения operator+ является частью библиотеки String, поэтому нет ничего страшного в том, чтобы объявить эту функцию другом и вручить ей ключи к внутреннему устройству String.

Внешними функциями могут перегружаться любые операторы, кроме операторов преобразования, =, [], () и -> - все эти операторы должны перегружаться только функциями класса.

Перегрузка операторов в форме функций класса

Синтаксис напоминает обычную перегрузку функций класса, разве что количество аргументов уменьшается на 1 по сравнению с формой внешней функции.

class String { private:

char* s; public:

Конструкторы и т.д.

String& operator+(const String&) const;

String& String::operator+(const String& s1) const

char* s2 = new char[str1en(s1.s) + strlen(s) + 1]; strcat(s2, s1, s); String newStr(s2); delete s2; return newStr;



1 ... 8 9 10 [ 11 ] 12 13 14 ... 82

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