C++/Не злоупотребляйте перегрузкой операторов

Кому-то может показаться прекрасной идеей перегрузить для своего контейнера оператор & для добавления, а оператор | для удаления элементов. Другой может воспользоваться для этого операторами + и -. Однако почти всегда предпочтительней использовать просто функции: push/pop, add/remove и т.п. Имена функций, в отличии от операторов, более наглядно отображают суть действия. И при обращении к объекту через указатель, его не требуется разыменовывать:

MyStack<int> *ptrStack = &stack;

// Допустим, класс MyVector предоставляет перегруженный
// оператор += для добавления нового элемента

stack += 10;    // Пока все нормально
ptrStack += 10; // Ой! Это не добавление, а изменение указателя.
(*ptrStack) += 10; // Теперь правильно. Но выглядит ужасно!

// А теперь используя методы
stack.push( 10 ); // Все нормально
ptrStack->push( 10 ); // И тут все нормально

По этой же причине, при создании класса контейнера, т.е. с перегруженным оператором [ ], следует также реализовывать метод at.

MyVector<int> *vector = new MyVector<int>();
...
int a = vector[10];    // Ошибка, это не вызов перегруженного оператора.
int b = (*vector)[10]; // Теперь правильно, но также некрасиво.
int c = vector->at(10); // Так все-таки лучше.

Особенно неприятна перегрузка операторов && и ||. Все мы знаем и широко используем одно из свойств языка C++, которое гарантирует, что в случае сравнения, если левая часть выражения будет равна false, то вторая не будет вычислена (т.е. ленивое вычисление). И используем это примерно вот так:

const char *p = ...
...
if( p && strlen(p) )
    // что-то выполняем

Так вот, в случае, когда компилятор встречает перегруженную версию операторов && или || это больше не работает. Они полностью меняют свое поведение. После перегрузки это будет как вызов метода, все аргументы которого вычисляются. И при этом в произвольном порядке.

Не менее неприятное поведение произойдет при перегрузке оператора , (запятая). Стандартное его поведение гарантирует, что выражения, разделенные оператором запятая, будут сделаны слева направо:

int a = func2(), func1();

До перегрузки стандарт гарантирует следующую последовательность вызовов: func1() –> func2(). После перегрузки оператор должен быть вызван как отдельная функция или метод класса, в качестве аргументов которому будут переданы результаты вызовов funcX(). А порядок вычисления аргументов не определен.

Популярные страницы

C++/Оптимизация программ на языке C++

C++/Компиляторы

C++/Профайлеры

C++/Отладчики

Неблокируемые и асинхронные приложения

C++/Что предпочтительней: ссылка или указатель?

C++/Необычный код на C++

У вас есть полезные знания?

Тогда поделитесь ими с другими, добавив статью