-
Notifications
You must be signed in to change notification settings - Fork 0
CPP 10. Перегрузка операторов в CPP. Операторы .*, *. Правила перегрузки операторов. Перегрузка операторов =, () и [ ]. Перегрузка операторов `стрелочка`, * и `стрелочка`*.
Перегрузка операторов - это удобный механизм, но он не относится к объектно-ориентированной части. Эта вещь уже была реализована в необъектных языках.
- Мы создаем свое данное. Почему бы нам для этого данного не определить какие-либо операции? Ведь для стандартных данных они есть.
- Мы должны задать знак операции, сказать, какая это операция (указать арность). Она может быть унарной, бинарной, тернарной. Операция может выполняться либо слева направо, либо справа налево. Операции имеют приоритет.
- Мы не можем задавать новые операторы, а на основе существующих операторов создавать новые операции, то есть перегружать оператор.
-
.
- доступ к члену объекта. Если мы перегрузим этот оператор, мы не сможем вызвать для объекта ни одного метода! -
.*
- указатель на метод. -
::
- оператор доступа к контексту. Он применяется для доступа к членам через имя класса или для доступа к глобальному контексту. -
? :
- тернарный оператор. Разработчики просто не смогли придумать, как перегрузить этот оператор. Страуструп не пришел ни к одному из решений. -
sizeof
- определение размера объекта. Если мы перегрузим, мы такое вытворим в программе! -
typeid
- возвращает id типа объекта. Если мы перегрузим, мы не сможем идентифицировать объект и понять, какого он типа.
Указатель на метод:
auto pf = &A::f();
A obj;
(obj.*pf)(); // .* имеет ниже приоритет чем ()
->*
аналогичен .*
, только работаем с указателем на объект.
-
=
,()
,[]
,->
,->*
перегружаются только как члены класса - Бинарные операторы можно перегружать или как члены класса (более предпочтительно), или как внешние функции
- Унарные перегружаем как члены класса.
-
&&
,||
,,
,&
особенность операторов в том, что второй операнд может не вычисляться, но при перегрузке он будет вычислен обязательно
Array& Array::operator=(const Array& arr)
{
if( this == &arr ) return *this;
if ( this->count != arr.count)
{
delete []this->mas;
this->count = arr.count;
this->mass = new double[this->count];
}
memcpy(this->mass, arr.mass, this->count * sizeof(double)); // плохая функция, но лень цикл писать
return *this;
}
Array& Array::operator=(Array&& arr)
{
if( this == &arr ) return *this;
if ( this->count != arr.count)
{
delete []this->mas;
this->count = arr.count;
}
this->mass = arr.mas;
arr.mas = nullptr;
return *this;
}
#include <iostream>
class MyFunctor {
public:
void operator()(int x, int y) const {
std::cout << "Sum = " << x + y << std::endl;
}
};
int main() {
MyFunctor myFunctor;
myFunctor(3, 4);
return 0;
}
double& Array::operator[](const Index& index)
{
if (index < 0 || index >= cnt) throw std::out_of_range("Error: class Array operator [];");
return mas[index];
}
Оператор ->
перегружается как член класса, он унарный, принимающий один параметр - в данном случае this будет принимать, и должен возвращать либо указатель, либо ссылку на объект.
class A
{
public:
void f();
};
class B
{
public:
A* operator->();
};
// Создадим объект где-то в коде и вызовем оператор ->
// Такая запись будет означать: оператор возвращает указатель на объект класса A
// и для него, для объекта, мы вызываем метод f()
B obj;
obj->f(); // (obj.operator->())->f()
#include <iostream>
class MyPointer {
private:
int* ptr;
public:
MyPointer(int* p) : ptr(p) {}
int& operator*() const {
return *ptr;
}
};
int main() {
int x = 42;
MyPointer ptr(&x);
std::cout << "x = " << *ptr << std::endl;
*ptr = 17;
std::cout << "x = " << *ptr << std::endl;
return 0;
}
Оператор ->*
перегружается как член класса и является бинарным (*this и указатель на метод)
class Callee
{
private:
int index;
public:
Callee(int i = 0) : index(i) {}
int inc(int d) { return index += d; }
};
class Caller
{
public:
typedef int (Callee::*FnPtr)(int); // указан тип
private:
Callee* pobj;
FnPtr ptr;
public:
Caller(Callee* p, FnPtr pf) : pobj(p), ptr(pf) {}
int operator ()(int d) { return (pobj->*ptr)(d); } // functor
};
class Pointer
{
private:
Callee* pce;
public:
Pointer(int i) { pce = new Callee(i); }
~Pointer() { delete pce; }
Caller operator->*(Caller::FnPtr pf) { return Caller(pce, pf); } // принимающий указатель на метод
};
void main()
{
Caller::FnPtr pn = &Callee::inc;
Pointer pt(1);
cout<<"Result: "<<(pt->*pn)(2)<<endl; // (pt.operator->*(pn)).operator()(2)
}
Этот класс Pointer
по существу скрывает связь объекта который выбирает связку и указателя на метод. Создаем объект и вызывая перегруженный оператор, он возвращает нам объект, который отвечает за связь и этот объект имеет перегруженный оператор круглые скобочки. Он уже вызывает указатель.