c++重载运算符

首先一点:重载运算符的本质其实是一种函数,只是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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值