530-C++运算符的重载(复数类和string类的实现,迭代器基础)

C++的运算符重载

使对象的运算表现得和编译器内置类型一样

template<typename T>
T sum(T a, T b)
{
	return a+b;//a.operator+(b)
}

如果T是编译器的内置类型的话,编译器对a+b是可以做的。
但是如果T是我们自定义的对象类型,a和b是两个对象,对象和对象之间该怎么相加???编译器是不知道的。
这里就要使用运算符的重载了。

return a+b;//a.operator+(b)

加法运算符的重载函数。

复数类的实现(运算符重载)

在这里插入图片描述
这个构造函数相当于可以构造3种不同的复数对象。

#include <iostream>
using namespace std;

class CComplex
{
public:
	//CComplex() CComplex(20) CComplex(30, 30)
	CComplex(int r = 0, int i = 0)
		:mreal(r), mimage(i) {}
	//指导编译器怎么做CComplex类对象的加法操作
	//CComplex operator+(const CComplex &src)
	//{
	    //写法1: 
		//CComplex comp;//局部对象 
		//comp.mreal = this->mreal + src.mreal;
		//comp.mimage = this->mimage + src.mimage;
		//return comp;
		//写法2: 
		//return CComplex(this->mreal + src.mreal,
			//this->mimage + src.mimage);//对象的优化 
	//}
	CComplex operator++(int)
	{
		//方法1: 
		//CComplex comp = *this;
		//mreal += 1;
		//mimage += 1;
		//return comp;
		//方法2: 
		return CComplex(mreal++, mimage++);
	}
	CComplex& operator++()
	{
		mreal += 1;
		mimage += 1;
		return *this;
	}
	void operator+=(const CComplex &src)
	{
		mreal += src.mreal;
		mimage += src.mimage;
	}
	void show() { cout << "real:" << mreal << " image:" << mimage << endl; }
private:
	int mreal;//实部 
	int mimage;//虚部 

    //这个全局的方法成了这个类的朋友,这个全局方法可以访问这个类的实部和虚部 
	friend CComplex operator+(const CComplex &lhs, const CComplex &rhs);
	friend ostream& operator<<(ostream &out, const CComplex &src);
	friend istream& operator>>(istream &in, CComplex &src);
};

CComplex operator+(const CComplex &lhs, const CComplex &rhs)//加法的全局运算符重载 
{
	return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}

ostream& operator<<(ostream &out, const CComplex &src)//输出是从左向右运算的 
{
	out << "mreal:" << src.mreal << " mimage:" << src.mimage << endl;
	return out;
}

istream& operator>>(istream &in, CComplex &src)
{
	in >> src.mreal >> src.mimage;
	return in;
}
int main()
{
	CComplex comp1(10, 10);
	CComplex comp2(20, 20);
	
	//comp1.operator+(comp2) 加法运算符的重载函数
	CComplex comp3 = comp1 + comp2;//左边是对象,优先调用成员方法 ,成员方法没有的话就去找全局方法 
	comp3.show();
	CComplex comp4 = comp1 + 20;//左边是对象,优先调用成员方法,成员方法没有的话就去找全局方法 
	//comp1.operator+(20) int->CComplex 编译器会找有没有CComplex(int)生成1个临时对象 ,然后进行相加 
	comp4.show();
	//编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法);如果没有成员方法
	//就在全局作用域找合适的运算符重载函数
	//  我们在全局提供   ::operator+(30, comp1)  30和comp1都当做实参传进去 
	CComplex comp5 = 30 + comp1;//30在里面必须要有趋势要做类型的转换!!! 无法调用成员方法的加法运算符重载函数 
	//看复数类型有没有带整型参数的构造函数来生成1个临时对象让lhs引用,就生成一个实部是30,虚部是0的临时对象 
	comp5.show();

	//CComplex operator++(int)
	comp5 = comp1++;
	// ++ --是单目运算符  operator++()表示前置++  operator++(int)表示后置++ 这个int只是为了区分,没有任何作用 
	comp1.show(); 
	comp5.show();
	//CComplex operator++()
	comp5 = ++comp1;
	comp1.show();
	comp5.show();

	//void comp1.operator+=(comp2)   ::operator+=(comp1, comp2)
	comp1 += comp2;
	
	//comp1.show();//对象信息的输出
	//这个输出<<运算符重载,对象不在左边,所以不可以提供成员方法 ,所以定义成全局方法 
	//cout ::operator<<(cout, comp1) 流对象是不断放,取东西,是变化的,不能加const  
	//void << endl; 因为要连续输出,不能用void作为返回值 
	//连续的输出,应该把cout返回:ostream& operator<<(ostream &out, const CComplex &src)
	cout << comp1 << endl;
	cin >> comp1 >> comp2;
	cout << comp1 << comp2 << endl;

    return 0;
}

在这里插入图片描述
在这里插入图片描述

C++string类的实现

在这里插入图片描述
这个写法的效率不好。ptmp指向new出来的内存,然后字符串拷贝,连接,当做参数传入tmp对象,tmp构造的时候又会根据传进来的指针进行判空,然后开辟内存,进行字符串拷贝,然后把这个ptmp指针delete掉,tmp是局部对象,return tmp,析构的时候又要把刚才构造时new出来的空间delete掉,2次new2次delete,太麻烦了。
我们应该这么做:

String operator+(const String& lhs, const String& rhs)
{
	//char *ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	String tmp;
	tmp._pstr = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	strcpy(tmp._pstr, lhs._pstr);//拷贝函数 
	strcat(tmp._pstr, rhs._pstr);//连接函数 
	//delete[]ptmp;
	return tmp;
}

这样就比刚才少了1次的new和delete操作。
但是是临时对象,还是要进行大量的拷贝构造,优化的方法是调用右值引用构造。

迭代器是容器类型的嵌套类。
我们假设有一个string对象,里面有一堆字符串,这些内容我们是看不见,因为是私有的。
在这里插入图片描述
我们想迭代string对象底层的字符串的字符,用迭代器是怎么做到的?
容器有一个begin方法,返回的是底层首元素的迭代器。
it指向了字符串底层的首元素。
容器的end方法,返回的是最后一个元素的后继位置的迭代器。
在这里插入图片描述
我们只需要不断的遍历,遍历到最后一个元素,往后再走,遇到end()迭代器,就退出。
++it,就是从当前元素跳到下一个元素去遍历。不需要管底层的数据结构。我们作为使用者只需要用迭代器++,不同的数据结构的差异都封装在迭代器++的重载运算符函数里面。
对迭代器解引用,就是访问迭代器访问的底层的数据。
在这里插入图片描述
不同容器底层的数据结构不一样,每一种容器都有自己的迭代器,设置成嵌套类型。
在这里插入图片描述
迭代器用的都是前置++,因为返回的是*this,效率比较高,不用生成临时对象。
迭代器的功能:提供一种统一的方式,来透明的遍历容器,我们不需要知道底层的数据结构,都封装在迭代器的++操作里面

#include <iostream>
#include <string>
//char arr[]="jkhsdkf";
using namespace std;

//自己实现一个字符串对象
class String
{
public:
	String(const char *p = nullptr)//构造函数 
	{
		if (p != nullptr)
		{
			_pstr = new char[strlen(p) + 1];
			strcpy(_pstr, p);
		}
		else
		{
			_pstr = new char[1];
			*_pstr = '\0';
		}
	}
	~String()//析构函数 
	{
		delete[]_pstr;
		_pstr = nullptr;
	}
	String(const String &str)//拷贝构造函数 
	{
		_pstr = new char[strlen(str._pstr) + 1];
		strcpy(_pstr, str._pstr);
	}
	String& operator=(const String &str)//赋值函数 
	{
		if (this == &str)
			return *this;

		delete[]_pstr;

		_pstr = new char[strlen(str._pstr) + 1];
		strcpy(_pstr, str._pstr);
		return *this;
	}
	bool operator>(const String &str)const
	{
		return strcmp(_pstr, str._pstr) > 0;
	}
	bool operator<(const String &str)const
	{
		return strcmp(_pstr, str._pstr) < 0;
	}
	bool operator==(const String &str)const
	{
		return strcmp(_pstr, str._pstr) == 0;
	}

	int length()const { return strlen(_pstr); }
	const char* c_str()const { return _pstr; }
	
	//char ch = str6[6];  可以修改:str6[6] = '7'
	char& operator[](int index) { return _pstr[index]; }
	//char ch = str6[6];  不允许修改!str6[6] = '7'
	const char& operator[](int index)const { return _pstr[index]; }

	//给String字符串类型提供迭代器的实现
	class iterator
	{
	public:
		iterator(char *p = nullptr) :_p(p) {}
		bool operator!=(const iterator &it)
		{
			return _p != it._p;
		}
		void operator++()
		{
			++_p;
		}
		char& operator*() { return *_p; }
	private:
		char *_p;
	};
	//下面2个方法是容器的方法哦!!! 
	//begin返回的是容器底层首元素的迭代器的表示
	iterator begin() { return iterator(_pstr); }
	//end返回的是容器末尾元素后继位置的迭代器的表示
	iterator end() { return iterator(_pstr + length()); }
private:
	char *_pstr;

	friend String operator+(const String &lhs, const String &rhs);
	friend ostream& operator<<(ostream &out, const String &str);
};
String operator+(const String &lhs, const String &rhs)
{
	//char *ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	String tmp;
	tmp._pstr = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	strcpy(tmp._pstr, lhs._pstr);//拷贝函数 
	strcat(tmp._pstr, rhs._pstr);//连接函数 
	//delete[]ptmp;
	return tmp;
}
ostream& operator<<(ostream &out, const String &str)
{
	out << str._pstr;
	return out;
}
int main()
{
	//迭代器的功能:提供一种统一的方式,来透明的遍历容器
	String str1 = "hello world!";//str1叫容器吗?叫。因为底层放了一组char类型的字符
	//容器的迭代器类型
	auto it = str1.begin();
	for (; it != str1.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;

	//c++11 foreach的方式来遍历容器的内部元素的值=>底层,还是通过迭代器进行遍历的
	for(char ch : str1)
	{
		cout << ch << " ";
	}
	cout << endl;

	//vector allocator  提供迭代器iterator的实现

	String str1;
	String str2 = "aaa";//string(const char*)
	String str3 = "bbb";
	String str4 = str2 + str3;
	String str5 = str2 + "ccc";
	String str6 = "ddd" + str2;

	cout << "str6:" << str6 << endl;
	//bool operator>(const String &str)
	if (str5 > str6)
	{
		cout << str5 << " > " << str6 << endl;
	}
	else
	{
		cout << str5 << " < " << str6 << endl;
	}

	int len = str6.length();
	for (int i = 0; i < len; ++i)
	{
		//char& str6.operator[](i)
		cout << str6[i] << " ";
	}
	cout << endl;

	//string -> char*
	char buf[1024] = { 0 };
	strcpy(buf, str6.c_str());//c_str()把对象管理的字符串返回成const char*类型 
	cout << "buf:" << buf << endl;

	return 0;
}

这2种写法等价。
在这里插入图片描述
在这里插入图片描述

auto自动根据等号右边的类型推导左边的类型,写起来比较简单方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林林林ZEYU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值