Могу ли я переопределить операторы на С++?
Я знал, что мы можем перегрузить операторы для класса. Но мой вопрос: могу ли я переопределить операторов?
Давайте рассмотрим, что у меня есть базовый класс и производный класс, возможно ли переопределить оператор, определенный (перегруженный) в базовом классе в производном классе (как при переопределении функции)?
Ответы
Ответ 1
Перегруженный оператор - это просто функция, поэтому она может быть виртуальной и переопределенной.
Но это редко хорошая идея.
Рассмотрим переопределенный оператор присваивания копии, который в каком-то производном классе проверяет, совместимо ли назначенное значение с назначенным объектом. Фактически он заменил проверку статического типа на динамическую проверку типов, что связано с большим количеством тщательного тестирования и статистической вероятностью правильности.
Пример непригодности:
#include <assert.h>
#include <iostream>
#include <string>
using namespace std;
struct Person
{
string name;
virtual
auto operator=( Person const& other )
-> Person&
{ name = other.name; return *this; }
Person( string const& _name ): name( _name ) {}
};
struct Employee: Person
{
int id;
auto operator=( Person const& other )
-> Person&
override
{
auto& other_as_employee = dynamic_cast<Employee const&>( other );
Person::operator=( other );
id = other_as_employee.id;
return *this;
}
auto operator=( Employee const& other )
-> Employee&
{
return static_cast<Employee&>(
operator=( static_cast<Person const&>( other ) )
);
}
Employee( string const& _name, int const _id )
: Person( _name )
, id( _id )
{}
};
void foo( Person& someone )
{
someone = Person( "Maria" ); // Fails, probably unexpectedly.
}
auto main() -> int
{
Person&& someone = Employee( "John", 12345 );
foo( someone );
}
Ответ 2
Вы можете добиться желаемого эффекта, предоставив виртуальную функцию "обложка" в базовом классе и вызовите ее из реализации оператора в базовом классе:
struct Base {
Base operator+(const Base& other) {
return add(other);
}
protected:
virtual Base add(const Base& other) {
cout << "Adding in Base code." << endl;
return Base();
}
};
struct Derived : public Base {
protected:
virtual Base add(const Base& other) {
cout << "Adding in Derived code." << endl;
return Derived();
}
};
int main() {
Base b1;
Base b2;
Derived d1;
Derived d2;
Base res;
res = b1+b2; // Prints "Adding in Base code."
res = b1+d2; // Prints "Adding in Base code."
res = d1+b2; // Prints "Adding in Derived code."
res = d1+d2; // Prints "Adding in Derived code."
return 0;
}
Демоверсия
Ответ 3
Я хочу добавить еще одну вещь: после моего личного разочарования по поводу поведения по умолчанию для некоторых операторов во встроенных типах я задался вопросом, можно ли было переопределить эти операторы простым и понятным способом. Ответ был мой проект Polyop, который достигает именно этого.
Итак, вы можете переопределить поведение по умолчанию операторов С++ по умолчанию? Да, просто оберните их так, чтобы операторский вызов был таким же, но тот факт, что он фактически называет совершенно другого оператора со свойствами и поведением, которые вы определили.
//Redefine the behavior of the int vs int equality operator
auto operator==(void(int,int) , pop::default_context )
{
return [](int lhs , int rhs )
{
return lhs * 2 == rhs;
};
}
using pop::triggers::_;
int main()
{
bool equal = _(4) == 2; //Returns true, since its the behavior we defined above
}
Все без каких-либо сбоев производительности.