运算符重载实质还是一个 函数。
通过重载运算符,可以让类在一些场景下使用起来更加方便。
返回值类型 operator op (参数);
示例:
ClassType& operator= (const ClassType& src); // 重载 “=” 运算符
即在类中定义一个成员函数来重载运算符。
示例:
#include
using namespace std;
class Demo
{
public:
Demo(int a)
{
this->a = a;
}
// 类成员函数实现运算符重载
Demo & operator+ (int n)
{
this->a += n;
return *this;
}
int getA()
{
return a;
}
private:
int a;
};
int main()
{
Demo demo(1);
// 重载 "+" 运算符后,对象可以直接加一个数
demo = demo + 2;
cout << demo.getA() << endl;
return 0;
}
在类外部定义一个函数来重载运算符,并将这个函数声明为该类的友元函数,以达到可以访问类的私有属性的目的。
示例:
#include
using namespace std;
class Demo
{
public:
// 友元函数实现运算符重载
friend Demo & operator+ (Demo & demo, int n);
Demo(int a)
{
this->a = a;
}
int getA()
{
return a;
}
private:
int a;
};
// 重载 "+" 运算符
Demo & operator+ (Demo & demo, int n)
{
demo.a += n;
return demo;
}
int main()
{
Demo demo(1);
// 重载 "+" 运算符后,对象可以直接加一个数
demo = demo + 2;
cout << demo.getA() << endl;
return 0;
}
定义:
顾名思义,单目运算符就是只对一个操作数进行操作
可重载的单目运算符:
++(递增)、--(递减)、*(解除引用)、->(成员选择)、!(逻辑非)、&(取址)、~(求反)、+(正)、-(负)。
递增和递减:
递增和递减分为前置递增递减和后置递增递减,两个的区别还是很明显。
class Test
{
public:
Test(int a)
{
this->a = a;
}
// 前置++
Test & operator++()
{
a++;
return *this;
}
// 前置--
Test & operator--()
{
a--;
return *this;
}
// 后置++
// 通过一个站位参数来和前置++区分开来
// 实现上,多了一个 tmp 变量,来实现先操作,后自增的效果
Test operator++(int)
{
Test tmp(*this);
this->a++;
return tmp;
}
// 后置--
Test operator--(int)
{
Test tmp(*this);
this->a--;
return tmp;
}
private:
int a;
};
分析:如果想执行递增运算,可使用 ++object,也可以使用 object++,但应尽量选择前者,这样避免创建一个未被使用的临时拷贝。
解除引用运算符(*)和成员选择运算符(->):
这两个运算符在智能指针类编程中应用最广,这里不详细介绍了。
智能指针是封装常规指针的类,简化了内存管理。
定义:
顾名思义,双目运算符就是对两个操作数进行操作
可重载的双目运算符:
运算符 |
名称 |
运算符 |
名称 |
---|---|---|---|
, |
逗号 |
< |
小于 |
!= |
不等于 |
<< |
左移 |
% |
求模 |
<<= |
左移并赋值 |
%= |
求模并赋值 |
<= |
小于或等于 |
& |
按位与 |
= |
赋值、复制赋值和移动赋值 |
&& |
逻辑与 |
== |
等于 |
&= |
按位与并赋值 |
> |
大于 |
* |
乘 |
>= |
大于或等于 |
*= |
乘并赋值 |
>> |
右移 |
+ |
加 |
>>= |
右移并赋值 |
+= |
加并赋值 |
^ |
异或 |
- |
减 |
^= |
异或并赋值 |
-= |
减并赋值 |
| |
按位或 |
->* |
指向成员的指针 |
|= |
按位或并赋值 |
/ |
除 |
|| |
逻辑或 |
/= |
除并赋值 |
[] |
下标运算符 |
加法、减法:
Date operator + (int daysToAdd) { // TODO }
Date operator - (int daysToAdd) { // TODO }
+=、-=、-=、*=、/=、%=、<<=、>>=、^=、|=、&=:
void operator += (int daysToAdd) { // TODO }
// 其余均和上式类似
等于(==)和不等于(!=),<、>、<=、>= 等:
bool operator== (const ClassType& compareTo) { // TODO }
bool operator!= (const ClassType& compareTo) { // TODO }
复制赋值运算符(=):
ClassType& operator= (const ClassType& copySource) {
if (this == ©Source) { // 先判断是否相等,避免重复赋值
return *this;
}
// 拷贝的逻辑实现,避免浅复制问题
return *this;
}
如果没有重载赋值运算符,系统会有一个默认的实现,但可能导致浅复制的问题。如果要实现深复制,就需要重载赋值运算符。
下标运算符([]):
const char& operator[] (int index) const {
return buffer[index];
}
逻辑与(&&)和逻辑或(||):
不推荐重载这两个运算符。
原因:我们知道 && 和 || 是有短路的,即 &&前面为假时,后面将不会计算了,同理 || 前面为真时也将跳过后面的计算。但是如果是重载了这两个运算符,则会失去短路效果。
成员运算符(.)、指针成员选择(.*)、作用域解析(::)、条件三目运算符(?:)、获取类型大小(sizeof)。
函数运算符 operator():
#include
using namespace std;
class Demo
{
public:
void operator () (const char *input) const {
cout << input << endl;
}
};
int main()
{
Demo demo;
// 调用函数运算符,参数为一个字符串
demo("hello world");
return 0;
}
operator() 让对象变得像函数,这个运算符也被称为 operator() 函数。
当然上例中的 operator() 的参数、返回值、实现都是可以根据具体需求改变的。
自定义字面量:
语法:
ReturnType operator "" YourLiteral(ValueType value) {
// conversion code here
}
在一些表示温度、千米等特殊的单位,C++ 本身是没有这些东西的,但是我们可以像下面这样定义自己的字面量:
// 都转换为以 “米” 为单位
long double operator"" _mm(long double x) { return x / 1000; }
long double operator"" _m(long double x) { return x; }
long double operator"" _km(long double x) { return x * 1000; }
参数 ValueType 只能是下面几个之一:
unsigned long long int、long double、char、wchar_t、char16_t、char32_t、
const char*、const char* 和 size_t、const wchar* 和 size_t、const char16_t* 和 size_t、const char32_t* 和 size_t,即:
void operator"" _test(unsigned long long int a) {}
void operator"" _test(long double a) {}
void operator"" _test(char a) {}
void operator"" _test(wchar_t a) {}
void operator"" _test(char16_t a) {}
void operator"" _test(char32_t a) {}
void operator"" _test(const char* a) {}
void operator"" _test(const char* a, size_t b) {}
void operator"" _test(const wchar_t* a, size_t b) {}
void operator"" _test(const char16_t* a, size_t b) {}
void operator"" _test(const char32_t* a, size_t b) {}
使用:
Temperature operator"" _C(long double celcius) {
return Temperature(celcius + 273);
}
Temperature t = 0.0_C;
使用 cout << obj << endl; 打印类对象:
#include
using namespace std;
class Test
{
public:
// 使用友元函数使其可以访问类的私有属性
friend ostream & operator << (ostream& os, Test & test);
Test(int a)
{
this->a = a;
}
private:
int a;
};
// 这里只能使用友元函数来实现,因为我们没法修改 cout 的左移函数的代码实现
ostream& operator << (ostream& os, Test & test)
{
os << test.a;
return os;
}
int main()
{
Test test(10);
cout << test << endl;
return 0;
}
最后,我们再来看一个 MyString 的实例,熟悉一下运算符重载的使用把。
MyString.h:
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
class MyString
{
public:
MyString(); // 无参构造方法 MyString str;
MyString(const char *str); // 有参构造方法 MyString str("hello world");
MyString(const MyString & str); // 复制构造方法 MyString str2(str1); MyString str2 = str1;
~MyString(); // 析构方法
MyString& operator= (const MyString & str); // 重载 "=" 运算符 str2 = str1;
MyString& operator= (const char *str); // 重载 "=" 运算符 str2 = "hello world";
char& operator[] (int index); // 重载 "[]" 运算符
bool operator==(const MyString & str); // 重载 "==" 运算符
bool operator!=(const MyString & str); // 重载 "!=" 运算符
bool operator>(const char *str); // 重载 ">" 运算符
bool operator<(const char *str); // 重载 "<" 运算符
bool operator>(const MyString & str); // 重载 ">" 运算符
bool operator<(const MyString & str); // 重载 "<" 运算符
friend ostream & operator<<(ostream & out, MyString & str); // 重载 "<<" 运算符,友元函数的实现方式
int length();
const char * c_str();
private:
char *m_str;
int m_len;
};
MyString.cpp:
#include "MyString.h"
MyString::MyString()
{
m_len = 0;
m_str = new char[m_len + 1];
strcpy(m_str, "");
}
MyString::MyString(const char * str)
{
m_len = str ? strlen(str) : 0;
m_str = new char[m_len + 1];
strcpy(m_str, str);
}
MyString::MyString(const MyString & str)
{
m_len = str.m_len;
m_str = new char[m_len + 1];
strcpy(m_str, str.m_str);
}
MyString::~MyString()
{
if (m_str)
{
delete [] m_str;
m_str = NULL;
m_len = 0;
}
}
MyString & MyString::operator=(const MyString & str)
{
if (this == &str)
{
return *this;
}
// 释放旧内存
if (m_str)
{
delete[] m_str;
m_len = 0;
}
m_len = str.m_len;
m_str = new char[m_len + 1];
strcpy(m_str, str.m_str);
return *this;
}
MyString & MyString::operator=(const char * str)
{
// 释放旧内存
if (m_str)
{
delete[] m_str;
m_len = 0;
}
m_len = str ? strlen(str) : 0;
m_str = new char[m_len + 1];
strcpy(m_str, str);
return *this;
}
char & MyString::operator[](int index)
{
return m_str[index];
}
bool MyString::operator==(const MyString & str)
{
if (m_len != str.m_len)
{
return false;
}
return strcmp(m_str, str.m_str) == 0;
}
bool MyString::operator!=(const MyString & str)
{
return !operator==(str);
}
bool MyString::operator>(const char * str)
{
return strcmp(m_str, str) > 0;
}
bool MyString::operator<(const char * str)
{
return strcmp(m_str, str) < 0;
}
bool MyString::operator>(const MyString & str)
{
return strcmp(m_str, str.m_str) > 0;
}
bool MyString::operator<(const MyString & str)
{
return strcmp(m_str, str.m_str) < 0;
}
ostream & operator<<(ostream & out, MyString & str)
{
out << str.m_str;
return out;
}
int MyString::length()
{
return m_len;
}
const char * MyString::c_str()
{
return m_str;
}