首先一点:重载运算符的本质其实是一种函数,只是c++编译器替我们调用而已。
1.重载+号运算符
+号运算符通常是类的创造者对于类的一种作用的封装,而想要你的类实现加法,那么
必须重载+号运算符,即使你不重载加号运算符,编译器也不会为你合成
看下面的代码:
#include<iostream>
using namespace std;
class Text
{
public:
Text(int _a = 10) :a(_a){}
Text operator+(const Text &);//重载加号运算符
private:
int a;
};
Text Text::operator+(const Text &a_text)//我们返回一个新的Text类
{
Text b_text;
b_text.a = this->a + a_text.a;
return b_text;
}
至于剩下的* / -原理都是一样的,我也就不写了。
2.后置++
我们思考一下,我们平常使用前置++的时候,是先使用没有++的值,那么
我们就应该明白,我们应该有一个临时量保留没有++前的值
看下面的代码
Text Text::operator++()
{
Text a_text = *this;//重载+号运算符我后面写
this->a++;
return a_text;
}
3.前置++
有了后置++的基础,那么前置++就很简单了。
看下面的代码
Text &Text::operator++(int)//注意这个int占位
{
this->a++;
return *this;
}
唯一不同的就是必须有这个int占位,否则是错误的。
4.=重载以及浅拷贝和深拷贝
当我们没有自己重载=号运算符的话,编译器会为我们提供一个默认的=号
重载运算符,不过我们不能信任编译器可以做的比我们自己更好。
因为这种=产生的拷贝是浅拷贝,看下面的代码
#include<iostream>
using namespace std;
class Text
{
public:
~Text()
{
delete p;
}
Text& operator=(const Text&);
void get_name()
{
p = new string("hello word");
cout << *p << endl;
}
int a;
string *p;
};
void main()
{
Text a_text, b_text;
b_text = a_text;//浅拷贝
a_text.get_name();
b_text.get_name();
system("pause");
}
这段代码我编译后过了2秒崩了的。原因在于,编译器的拷贝是直接让我们的p指针指向
helloword的内存,而我们最后结束程序的时候析构了两会,那么这肯定出错。
所以我们必须重载=号运算符来避免这种浅拷贝
看下面的代码
#include<iostream>
#include<string>
using namespace std;
class Text
{
public:
~Text()
{
cout << "析构函数被调用" << endl;
delete p;
}
Text(const string &s, int _a) :p(new string(s)), a(_a){}
Text() = default;
Text& operator=(const Text&);
int a;
string *p;
};
Text& Text::operator=(const Text&a_text)
{
auto newp=new string(*(a_text.p));//注意这里,这种方式是异常安全的
delete this->p;
this->p=newp;
a=a_text.a;
cout << "等号重载被调用" << endl;
return *this;
}
void main()
{
{ Text a("hello word", 5);
Text b;
b = a;
a = b; }
system("pause");
}
5.重载<<和>>运算符
一般来说,我们重载这两个运算符是给类定义适合对象的io操作
不过需要注意的是,这两个运算符的重载必须是全局函数,因为如果是成员函数的话,
格式是这样的Text>>cin显然是错误的。
istream& operator>>(istream& that, Text &a_text)
{
int _a;
string _s;
that >> _a >> _s;
if (that)//对于输入操作,最好检测一下流的状态
{
delete a_text.p;
a_text. p = new string(_s);
a_text.a = _a;
return that;
}
else
{
cout << "input error" << endl;
that.clear();//把流的状态复位
return that;
}
}
ostream &operator << (ostream &that, const Text & a_text)
{
that << a_text.a << " " << *(a_text.p);//输出操作尽量减少格式化操作
return that;
}
注意声明成友元函数。
6.重载==运算符
我们使用内置类型的时候==号是判断两边的变量是否相等。所以易知返回的是bool类型。
代码如下
bool Text::operator==(const Text &a_text)
{
if (a == a_text.a)
{
return true;
}
else
return false;
}
下一个是友元函数的版本
bool operator == (Text &a_text, Text &b_text)//声明成了友元函数
{
if (a_text.a == b_text.a)
return true;
else
return false;
}
至于剩余的> <号方法是一样的,根据自己的需求重载,
7.重载赋值运算符
除了拷贝赋值运算符和移动赋值运算符外,标准库vector还提供了第三种赋值运算符。
看下面的代码
Text& Text::operator=(initializer_list<string> a)//简单的示范,注意声明成成员函数
{
this->b = a;
return *this;
}
8.重载复合赋值运算符
复合赋值运算符不非得是类的成员,不过我们还是倾向于把包括符合赋值类型在内的
所有赋值运算都定义在类的内部。看下面的代码
Text& Text::operator+=(const Text &a_text)//我们可以思考一下内置类型的符合赋值运算
{
this->a += a_text.a;
return *this;
}
9.重载下标运算符
下标运算符必须是成员函数,为了与下标的原始定义兼容,下标运算符通常以访问元素的引用作为返回值
,这样做的好处是我们可以让其可以出现在赋值运算符的任意两端,既可以赋值给别人,
我们自己也可以改变这个成员的值。进一步,我们最好定义两个版本,一个返回普通引用,另
一个是类的常量成员并且返回常量引用。
看下面的代码
Text(int _a = 10, vector<string> _b = { "hello", "word" }) :a(_a), b(_b){}
string &operator[](size_t n)
{
return b[n];
}
const string&operator[](size_t n)const
{
return b[n];
}
public:
int a;
vector<string> b;
10.重载成员访问运算符
在迭代器及只能指针类中常用到解引用操作符和箭头操作符。
看下面的代码
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class MyString
{
public:
MyString() = default;
MyString(string *_s) :s(_s){}
string& operator*()
{
return *s;
}
string& operator*()const
{
return *s;
}
string*& operator->()
{
return s;
}
private:
string *s;
};
int main()
{
string s("hello");
MyString a(&s);
cout << *a << endl;
cout << a->size() << endl;
system("pause");
return 0;
}
这段代码显示了一个简单的用法,我们还可以这样用。
#include<iostream>
#include<string>
#include<vector>
#include<memory>
using namespace std;
class MyString
{
public:
MyString(vector<string> *_s = NULL) :s(_s){}
size_t size()
{
cout << "sja" << endl;
return s->size();
}
void push_back(string _s)
{
s->push_back(_s);
}
string& operator[](size_t _n)
{
n = _n;
return (*s)[n];
}
vector<string>& operator*()
{
return *s;
}
vector<string>*& operator->()
{
return s;
}
private:
vector<string> *s;
size_t n;
};
int main()
{
vector<string> a{"hello","word"};
MyString s(&a);
cout << s->size() << endl;
cout << (*s).size() << endl;
system("pause");
return 0;
}
11重载函数调用操作符
我们将定义一个打印string实参内容的类
看下面的代码
#include<iostream>
#include<string>
using namespace std;
class PrintString
{
public:
PrintString(ostream &_os = cout, char _c = ' ') :os(_os), c(_c){}
void operator()(const string &s)const//函数调用运算符必须是类的成员函数
{
os << s << c;
}
private:
ostream &os;//确定输出的流
char c;//用于将不同的输出隔开的字符
};
int main()
{
PrintString a;
string s = "hello";
a(s);//函数调用运算符的使用
system("pause");
return 0;
}