05 运算符重载

01 运算符重载

C++的运算符重载:使对象的运算表现的和编译器内置类型一样

template <typename T>
T sum(T a, T b)
{
	return a + b; // a.operator+(b) a调用加法函数,把b当实参传入
}

上面代码中的a,b没有指定类型,可以任意实例化类型。如果a + b是普通类型,内置编译器可以自行编译计算,但a和b是对象类型,编译器不知道对象如何相加,要用运算符重载。
复数类的实现例子

class CComplex
{
public:
	CComplex(int r = 0, int i = 0)
		:mreal(r), mimage(i) {}
	CComplex& operator+(const CComplex &src)
	{
		/*
		CComplex comp;
		comp.mreal = this->mreal + src.mreal;
		comp.mimage = this->mimage + src._mimage;
		return comp;
		*/
		// 优化
		return CComplex(this->mreal + src.mreal, this->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);
};

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

int main()
{
	CComplex comp1(10, 10);
	CComplex comp2(20, 20);

	// comp1.operator+(comp2) 加法运算符的重载函数
	// 也可以写成本质的形式 comp1.operator+(comp2)
	CComplex comp3 = comp1 + comp2;	
	comp3.show();
	CComplex comp4 = comp1 + 20; // comp1.operator+(20)
	comp4.show();

	/* 
	编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法);
	如果没有成员方法,就在全局作用域找合适的运算符重载函数。
	下面调用了全局运算符重载函数,把整型转成复数类型 ::operator(30,comp1)
	*/
	CComplex comp5 = 30 + comp1;
	comp5.show();

	return 0;
}

单目运算符: ++ /- -
operator++():扩号中无参数,为前置++。

CComplex& operator++()
{
	mreal += 1;
	mimage += 1;
	return *this;
}

operator++(int):括号中带一个整型参数的,为后置++。

CComplex operator++(int)
{
	/*
	CComplex comp = *this;  // 保留旧值 
	mreal += 1;
	mimage += 1;
	return comp;  // 返回原来的旧值 
	以下为优化
	*/
	return CComplex( mreal++,  mimage++ );
}
// CComplex operator++()
comp5 = ++comp1;

// CComplex operator++(int)
comp5 = comp1++;

复合赋值运算符:+=

// 全局作用域下
void operaotr+=(const CComplex &src)
{
	mreal += src.mreal;
	mimage += src.image;
}
// void comp.operator+=(comp2)  ::operator+=(comp1,comp2)
comp1 += comp2;

对象信息的输入和输出
为了使得和内置编译器同样的输入和输出形式
cin >> comp1; cout << comp1 << endl;
对此提供相应的输入和输出运算符重载函数,都是在全局作用域下实现的,如下

// 输入运算符重载函数 
istream& operator>>(istream &in,const CComplex &src)
{
	in >> src.mreal >> src.mimage;
	return in;
}

// 输出运算符重载函数
ostream& operator<<(ostream &out,const CComplex &src)
{
	out << "mreal:" <<  src.mreal << "mimage:" << src.mimage << endl;
	return out;
}

02 Strig类

1 模拟实现C++的string类代码

#include <iostream>
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 lengh()const { return strlen(_pstr); }
	// 返回C字符串
	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]; }
	
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];
	strcpy(ptmp, lhs._pstr);
	strcat(ptmp, rhs._pstr);
	String result(ptmp);
	delete[] ptmp; // 释放临时分配的内存
	return String(ptmp);
}
// 输出流运算符重载
ostream& operator<<(ostream& out, const String& str) 
{
	out << str._pstr;
	return out;
}

int main() {
	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;
	if (str5 > str6)
	{
		cout << str5 << ">" << str6 << endl;
	}
	else
	{
		cout << str5 << "<" << str6 << endl;
	}
	
	int len = str6.length();
	for (int i = 0; i < len; ++i)
	{
		cout << str6[i] << " ";	
	}
	cout << endl;

	// string -> char*
	char buf[1024] = { 0 };
	strcpy(buf, str6.c_str());
	cout << "buf:" << buf << endl;

	return 0;
}

上面代码中的加法运算符重载函数效率低,含有两次的内存构造和释放,对此改为只有一次的内存构造和释放

String operator+(const String& lhs, const String& rhs) {
	String tmp;
	tmp._pstr = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	strcpy(ptmp, lhs._pstr);
	strcat(ptmp, rhs._pstr);
	return String(ptmp);
}

2 string字符串对象的迭代器iterator实现

优化效率

#include <iostream>
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 lengh()const { return strlen(_pstr); }
	// 返回C字符串
	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;
	};
	//begin返回的是容器底层首元素的迭代器的表示
	iterator begin() { return iterator(_pstr); }
	//end返回的是容器末层元素后继位置的迭代器的表示
	iterator end() { return iterator(_pstr + lengh()); }

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(ptmp, lhs._pstr);
	strcat(ptmp, rhs._pstr);
	// delete[] ptmp; // 释放临时分配的内存
	return String(ptmp);
}
// 输出流运算符重载
ostream& operator<<(ostream& out, const String& str) {
	out << str._pstr;
	return out;
}

int main() {
	//迭代器的功能:提供一种统一的方式,来透明的遍历容器
	String str1 = "hello world!";
	// 容器迭代器的类型
	String::iterator it = str1.begin();
	for (; it != str1.end(); ++it)
	{
		cout << *it << " ";
	}
	cout << endl;

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

03 迭代器的失效问题

迭代器失效问题
一. 迭代器为什么会失效?
1. 当容器调用rease方法后,当前位置到容器末尾元素的所有的迭代器全部失效了
2. 当容器调用insert方法后,当前位置到容器末尾元素的所有的迭代器全部失效了
迭代器依然有效 迭代器全部失效
首元素 -> 插入点/删除点 -> 末尾元素
3. insert来说,如果引起容器内存扩容
原来容器的所有的迭代器就全部失效了
首元素 -> 插入点/删除点 -> 末尾元素
4. 不同容器的迭代器是不能进行比较运算的

二. 迭代器失效以后,问题该怎么解决?
对插入/删除点的迭代器进行更新操作

int main() {
	vector<int> vec; // 向量
	for (int i = 0; i < 20; ++i) {
		vec.push_back(rand() % 100 + 1); // push_back() 在Vector最后添加一个元素
	}

	for (int v : vec) {
		cout << v << " ";
	}
	cout << endl;
	for (int v : vec) 
	{
		cout << v << " ";
	}
	cout << endl;
	return 0;
}

给vec容器所有的偶数前面添加一个偶数值小于1的数字

	auto it = vec.begin();
	for (; it != vec.end(); ++it) {
		if (*it % 2 == 0) {
			vec.insert(it, *it - 1);
			break;
		}
	}

修改后的代码
给vec容器所有的偶数前面添加一个偶数值小于1的数字

	auto it = vec.begin();
	for (; it != vec.end(); ++it) {
		if (*it % 2 == 0) {
			it = vec.insert(it, *it - 1);
			++it;
			//break;
		}
}

把vec容器中所有的偶数全部删除

	auto it = vec.begin();
	for (; it != vec.end(); ++it) {
		if (*it % 2 == 0) {
			//迭代器失效的问题,第一次调用erase以后,迭代器it就失效了
			vec.erase(it); // insert(it, val)  erase(it)
			//break;
		}
	}

修改后的代码
把vec容器中所有的偶数全部删除

auto it = vec.begin();
while ( it != vec.end())
{
	if (*it % 2 == 0) 
	{
			it = vec.erase(it); // insert(it, val)  erase(it)
			//break;
	}
	else
	 {
			++it;
		}
}
	for (int v : vec) 
	{
		cout << v << " ";
	}
	cout << endl;

04

new和delete

  1. malloc和new的区别?
    a. malloc按字节开辟内存的;new开辟内存时需要指定类型 new int[10]
    所以malloc开辟内存返回的都是void* operator new -> int*
    b. malloc只负责开辟空间,new不仅仅有malloc的功能,可以进行数据的初始化

    new int(20);    
    new int[20]();      
    int()
    

    c. malloc开辟内存失败返回nullptr指针;new抛出的是bad_alloc类型的异常

  2. free和delete的区别?
    delete (int*) p:先调用析构函数;再free(p)

new和delete能混用吗?C++为什么区分单个元素和数组的内存分配和释放呢?
new delete
new[] delete[]
对于普通的编译器内置类型 new/delete[] new[]/delete

自定义的类类型,有析构函数,为了调用正确的析构函数,那么开辟对象数组的时候,
会多开辟4个字节,记录对象的个数

// 先调用operator new开辟内存空间,然后调用对象的构造函数(初始化)
void* operator new(size_t size)
{
	void* p = malloc(size);
	if (p == nullptr)
		throw bad_alloc();
	return p;
}

// delete p; 调用p指向对象的析构函数,再调用operator delete施放内存空间
void operator delete(void* ptr)
{
	free(ptr);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值