Kami melanjutkan seri "C ++, menggali lebih dalam." Tujuan dari seri ini adalah untuk memberi tahu Anda sebanyak mungkin tentang berbagai fitur bahasa, mungkin sangat istimewa. Artikel ini membahas tentang kelebihan operator. Perhatian khusus diberikan pada penggunaan operator kelebihan beban di perpustakaan standar. Ini adalah artikel kedua dalam seri ini, yang pertama didedikasikan untuk fungsi dan templat yang berlebihan, ada di sini . Artikel selanjutnya akan fokus pada pernyataan manajemen memori yang berlebihan.
Daftar Isi
pengantar
Overloading operator adalah kemampuan untuk menerapkan operator bahasa bawaan untuk berbagai jenis, termasuk jenis pengguna. Sebenarnya, ini adalah ide yang cukup lama. Sudah dalam bahasa-bahasa pemrograman pertama, simbol dari operasi aritmatika: +
.-
, dll. digunakan untuk operasi pada bilangan bulat dan bilangan real, meskipun faktanya mereka memiliki ukuran yang berbeda dan representasi internal yang berbeda, dan oleh karena itu, operasi ini dilaksanakan dengan cara yang berbeda. Dengan munculnya bahasa-bahasa berorientasi objek, ide ini dikembangkan lebih lanjut. Jika operasi pada tipe yang ditentukan pengguna memiliki semantik yang serupa dengan operasi pada tipe built-in, maka mengapa tidak menggunakan sintaks dari operator built-in. Ini dapat meningkatkan keterbacaan kode, membuatnya lebih ringkas dan ekspresif, menyederhanakan penulisan kode umum. Dalam C ++, operator overloading memiliki dukungan serius dan secara aktif digunakan di perpustakaan standar.
1. Masalah umum kelebihan operator
1.1. Operator yang kelebihan beban
C++17 : +
, -
, *
, /
, %
, ^
, &
, |
, ~
, !
, ,
, =
, <
, >
, <=
, >=
, ++
, –-
, <<
, >>
, ==
, !=
, &&
, ||
, +=
, -=
, /=
, %=
, ^=
, &=
, |=
, *=
, <<=
, >>=
, []
, ()
, ->
, ->*
, new
, new[]
, delete
, delete[]
.
( , C++98.) , , , . , ()
, . +
, -
, *
, &
, ++
, –-
( ) — , , 6 .
1.2.
, . — +
+=
std::basic_string<>
. std::filesystem::path
(C++17). /
/=
. , . .
, . — <<
. , , , .
std::out<<c?x:y;
(std::out<<c)?x:y;
std::out<<(c?x:y);
.
std::out
void*
, - . -, , . +=
, , , , .
1.3. ,
: ,
(), &&
, ||
. , ( ), (short-circuit evaluation), , . ( , , , &&
, false
, ||
, true
.)
&
( ). &
, . ++11 ( ) std::addressof()
, &
.
1.4.
. , . , , . , , , . , , . a=b=c
. bool
. +
, -
, ~
. , . , , ( . [Sutter1]). , , . .
1.5.
1.5.1.
: - (-) . - — =
, ->
, []
, ()
. .
, - - operator@
, @
() . , . ()
.
, (-) , operator@
, @
() . , , . — , ( ), , . , , . :
namespace N
{
class X
{
X operator+() const;
X operator+(const X& x) const;
void operator()(int x, int y);
char operator[](int i);
};
X operator-(const X& x);
X operator-(const X& x, const X& y);
}
, , -, .
1.5.2.
(): . .
( , N
):
N::X x, y;
N::X z = x + y;
N::X v = x – y;
N::X w = +x;
N::X u = -x;
x(1,2);
char p = x[4];
N::X z = x.operator+(y);
N::X v = operator-(x, y);
N::X w = x.operator+();
N::X u = operator-(x);
x.operator()(1,2);
char p = x.operator[](4);
, , , . , , . ( ) , . ( — .) ->
, , .
, , (argument depended lookup, ADL), , , , , , . , ADL .
1.5.3.
, , - . . , , . . , . , .
2.
2.1.
. — const ( -), ( ). ()
.
std::string
+
: const std::string&
, const char*
.
3.4.2 ()
.
()
, .
2.2.
, , , .
2.2.1.
— . , x@y
, y@x
. , - . +
std::string
, const char*
.
2.2.2.
-. (, -, .) . -, , , -, . . 3.8.
2.2.3.
— . X
, , X
. , . , -, . .
int i = 42;
std::reference_wrapper<int> rwi(i);
std::cout << rwi << '\n';
std::reference_wrapper<int>
, int&
, . , , . , 2.3.
2.2.4.
, -, . 2.6.
2.3.
, , . , .
class X
{
friend X operator+(const X& x, const X& y)
{
}
};
. , , , . , . , [Meyers1]. +
friend
, -
.
template<typename T>
class Rational
{
T num;
T den;
public:
Rational(T n = 0, T d = 1) : num(n), den(d) {}
T Num() const { return num; }
T Den() const { return den; }
friend const Rational operator+(
const Rational& x, const Rational& y)
{
return Rational(
x.num * y.den + y.num * x.den,
x.den * y.den);
}
};
template<typename T>
const Rational<T>operator-(
const Rational<T>& x, const Rational<T>& y)
{
return Rational<T>(
x.Num() * y.Den() - y.Num() * x.Den(),
x.Den() * y.Den());
}
+
. , T
Rational
, . :
Rational<int> r1(1, 2), r2(31, 64);
Rational<int> r3 = r1 + r2;
Rational<int> r4 = r1 + 3;
Rational<int> r5 = 4 + r2;
Rational
int
. int
int
Rational
, .
-
.
Rational<int> r6 = r1 - 3;
Rational<int> r7 = 4 - r2;
, int
Rational
. , , -
:
template<typename T>
const Rational<T> operator-(const Rational<T>& x, T y)
{
return operator-(x, Rational<T>(y));
}
template<typename T>
const Rational<T> operator-(T x, const Rational<T>& y)
{
return operator-(Rational<T>(x), y);
}
. [Meyers1].
2.4.
, , (computational constructor). (return value optimization, RVO). . [Dewhurst].
2.5.
-, . , , ( NVI – non virtual interface). «» . . , , *a+*b
. , ( +
), , ( Visitor). , , . [Dewhurst]. . , , — .
2.6.
, , . :
enum class Color { Begin, Red = Begin, Green, Blue, End};
Color& operator++(Color& col) { return (Color&)(++(int&)col); }
:
void Foo(Color col);
for (Color col = Color::Begin; col < Color::End; ++col)
{
Foo(col);
}
Color operator*(Color col) { return col; }
:
std::for_each(Color::Begin, Color::End, Foo);
. :
struct Colors
{
Color begin() const { return Color::Begin; }
Color end() const { return Color::End; }
};
for
:
for (auto col : Colors())
{
Foo(col);
}
3.
, .
3.1. ->
- ( ). (, ), , ->
. «» — . :
class X
{
void Foo();
};
class XPtr
{
X* operator->() const;
};
X x;
x->Foo();
x.operator->()->Foo();
->
.
3.2. *
->
. , , ->
. -.
*
.
3.3. []
, , , -, . , , , . «» , , . . , , , , &[i]
, . , , .
— .
T& operator[](int ind);
const T& operator[](int ind) const;
, -.
std::vector<>
, std::array<>
, std::basic_string<>
, std::deque<>
std::map<>
, std::unordered_map<>
. std::unique_ptr<>
.
3.3.1.
C++ , a[i,j]
, « », a[i][j]
. -. .
template<typename T>
class Matrix
{
public:
Matrix(int rowCount, int colCount);
class RowProxy;
RowProxy operator[](int i) const;
class RowProxy
{
public:
T& operator[](int j);
const T& operator[](int j) const;
};
};
Matrix<double> mtx(5, 6);
double s = mtx[1][2];
mtx[2][3] = 3.14;
3.4. ()
-. , . , ()
, , . C++ . C++ . , , : , , -, , . , . , , ( ), ()
-. — std::for_each()
, .
3.4.1. -
C++ ( ). . - « ».
3.4.2.
()
, . . .
3.4.3. -
( std::unordered_set<>
, std::unordered_multiset<>
, std::unordered_map<>
, std::unordered_multimap<>
) , - . -. ()
- std::size_t
. , . std::hash<>
, . , . , , -. .
- .
- .
C- .
3.4.4.
, . . ()
, , bool
. , . std::less<>
std::equal_to<>
, . <
, ==
.
std::less<>
std::set<>
, std::multiset<>
, std::map<>
, std::multimap<>
, std::priority_queue<>
.
std::equal_to<>
std::unordered_set<>
, std::unordered_multiset<>
, std::unordered_map<>
, std::unordered_multimap<>
.
, .
- , . , , , , ( ).
- , .
- .
C- .
3.4.5.
, . , (deleter). ()
. std::unique_ptr<>
std::default_delete<>
, delete
( delete[]
). std::shared_ptr<>
delete
. , . .
std::unique_ptr<>
-.- .
- std::shared_ptr<>
, std::shared_ptr<>
.
3.4.6.
, , . , ( ), .
Jika objek fungsional yang diperlukan tidak ditentukan, operator <
digunakan secara default dalam algoritma std::lexicographical_compare()
yang membandingkan rentang, dalam algoritma pencarian minimum / maksimum ( min_element()
, dll), dalam algoritma yang terkait dengan pengurutan dan pengurutan data ( std::sort()
, dll), dalam algoritma terkait piramida ( std::make_heap()
, dll).Operator ==
digunakan secara default dalam algoritma std::equal()
yang membandingkan rentang, dalam algoritma std::count()
yang menghitung jumlah elemen tertentu, dalam algoritma pencarian ( std::find()
, dll), dalam algoritma std::replace()
dan std::remove()
yang memodifikasi rentang.
Operator +
digunakan secara default dalam algoritma accumulate()
. (Untuk perinciannya, lihat Lampiran A.)
, .
- , . , .
- , . ( -.)
C- .
3.4.7.
C++11 . ()
. , - . .
#include <functional>
int Foo(const char* s) { return *s; }
struct X
{
int operator() const (const char* s) { return *s; }
};
std::function<int(const char*)>
f1 = Foo,
f2 = X(),
f3 = [](const char* s) { return *s; };
int r1 = f1("1"),
r2 = f2("2"),
r3 = f3("3");
3.5.
. bool
.
<
==
, . . (. [Josuttis]). <
: ( x<y
true
, y<x
false
), ( x<y
y<z
, x<z
), (x<x
false
), ( !(x<y) && !(y<x)
!(y<z) && !(z<y)
, !(x<z) && !(z<x)
). ==
: ( x==y
, y==x
), ( x==y
y==z
, x==z
), (x==x
true
). , . , .
. . <
==
, !
. , . std::rel_ops
<=
, >
, >=
, !=
. ( <utility>
.) .
#include <utility>
class X { };
bool operator==(const X& lh, const X& rh);
bool operator<(const X& lh, const X& rh);
bool operator<=(const X& lh, const X& rh)
{
return std::rel_ops::operator<=(lh, rh);
}
, std::rel_ops
, using
-:
using namespace std::rel_ops;
— <
, <=
, >
, >=
, ==
, !=
, , : std::thread::id
, std::type_index
, std::monostate
. , , . .
==
!=
std::error_code
, std::bitset
. , <
.
3.6.
, +
+=
. , — -. , +
, -
, -. .
class X
{
const X operator-() const;
X& operator+=(const X& x);
};
const X operator+(const X& x, const X& y);
+
, -
( ) . . , , . -, *this
. +
+=
, 1.4.1, , X
, const
.
. C-, . (. 2.2).
std::complex<>
. +
, +=
, -
, -=
. std::basic_string<>
+
+=
. std::filesystem::path
(C++17). /
/=
. , . .
3.7. ,
. , . - . .
class Iter
{
public:
Iter& operator++()
{
return *this;
}
const Iter operator++(int)
{
Iter it(*this);
++*this;
return it;
}
};
, , , . 1.4.
, .
3.8. << >>
( ). , : , , . .
#include <iostream>
struct Point
{
int X;
int Y;
};
std::ostream& operator<<(std::ostream& strm, const Point& p)
{
strm << '[' << p.X << ',' << p.Y << ']';
return strm;
}
— , , . void*
, - . . 1.2.
3.9.
, -, . , , , . / . , . — . « », , ( ), , .
— , .
class X
{
public:
X(const X& src);
X(X&& src) noexcept;
X& operator=X(const X& src);
X& operator=X(X&& src) noexcept;
};
, , . - noexcept
.
. "=default"
.
class X
{
public:
X& operator=X(const X& src) = default;
X& operator=X(X&& src) = default;
};
. , , .
class X
{
public:
X& operator=X(const X& src) = delete;
X& operator=X(X&& src) = delete;
};
. , *this
. , a=b=c
. — « ». - , .
class X
{
public:
void Swap(X& src) noexcept;
X(const X& src);
X(X&& src) noexcept;
X& operator=X(const X& src);
X& operator=X(X&& src) noexcept;
};
:
X& X::operator=X(const X& src)
{
X tmp(src);
Swap(tmp);
return *this;
}
X& X::operator=X(X&& src) noexcept
{
X tmp(std::move(src));
Swap(tmp);
return *this;
}
, .
: , , ( ).
« » , .
X& X::operator=X(const X& src)
{
if (this != std::addressof(src))
{
}
return *this;
}
, , . « » , , , , .
, , .
X& X::operator=X(const X& src)
{
if (this != std::addressof(src))
{
this->~X();
new (this)(src);
}
return *this;
}
this
, new
, this
, . , , , . , , this
, , . . , X
- X
. this->~X()
, . .
( ) , /. ( .) , . .
3.10. !
, . true
, («», «»). . explicit
bool
, , true
, .
explicit operator bool() const noexcept;
4.
. , .
.
.
std::variant<>
(C++17). std::visit()
, ()
std::variant<>
, . std::visit()
()
, std::variant<>
. .
using IntStr = std::variant<int, std::string>;
struct Visitor
{
void operator()(int x) const
{
std::cout << "int, val=" << x << '\n';
}
void operator()(const std::string& x) const
{
std::cout << "string, val=" << x << '\n';
}
};
IntStr a(42), b("meow");
Visitor v;
std::visit(v, a);
std::visit(v, b);
std::reduce()
(C++17). std::accumulate()
. std::accumulate()
.
template<class InputIt, class T, class BinOper>
T accumulate(InputIt first, InputIt last, T init, BinOper oper);
BinOper
— ,
T f(T t, S s);
T
— , S
— , T
. , , T
S
, . , , . init
. , C++17 std::reduce()
, , .
template<class ExecutionPolicy,
class InputIt, class T, class BinOper>
T reduce(ExecutionPolicy&& policy,
InputIt first, InputIt last, T init, BinOper oper);
BinOper
std::accumulate()
— , BinOper
:
T f(T t, S s);
T f(S s1, S s2);
T f(T t1, T t2);
. - C-
C- — , — T*
const T*
, T
(char
, wchar_t
, etc). std::hash<>
, std::less<>
std::equal_to<>
, , . .
#include <functional>
template <class T>
inline void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 +
(seed << 6) + (seed >> 2);
}
#include <cstring>
namespace std
{
template <>
struct hash<const char*>
{
size_t operator()(const char* str) const
{
std::size_t hash = 0;
for (; *str; ++str)
{
hash_combine(hash, *str);
}
return hash;
}
};
template <>
struct equal_to<const char*>
{
bool operator()(const char* x, const char* y) const
{
return strcmp(x, y) == 0;
}
};
template <>
struct less<const char*>
{
bool operator()(const char* x, const char* y) const
{
return strcmp(x, y) < 0;
}
};
}
hash_combine()
— Boost. -.
, , C- - .
#include <cstring>
const char* cc[] = { "one", "two", "three", "four" };
std::sort(cc, cc + _countof(cc),
[](const char* x, const char* y)
{ return std::strcmp(x, y) < 0; });
[Josuttis]
Josattis, Nikolai M. C ++ Perpustakaan Standar: Panduan Referensi, edisi kedua: Per. dari bahasa Inggris - M.: LLC “Saya. Williams ", 2014.
[Dewhurst]
Dewhurst, Stefan K. Slippery menempatkan C ++. Cara menghindari masalah saat merancang dan menyusun program Anda: Per. dari bahasa Inggris - M .: DMK Press, 2012.
[Meyers1]
Meyers, Scott. Penggunaan C ++ secara efektif. 55 cara pasti untuk meningkatkan struktur dan kode program Anda:: Per. dari bahasa Inggris - M .: DMK Press, 2014.
[Sutter1]
Sutter, Lambang. Solusi masalah kompleks dalam C ++.: Per. dari bahasa Inggris - M: LLC “Saya. Williams, 2015.