第十一章 类和对象——第5节 运算符重载

11.5 运算符重载

作用:通过函数对运算符进行重新定义,实现对自定义数据类型的操作

注意:只能对自定义数据类型进行操作

11.5.1 算数运算符重载

语法:operator算数符号(),其中算数符号有+ - * / % ++ –

用法:例如使用operator+()对加号进行重载后,Person类的实例化对象可直接使用+相加,有“成员函数”与“全局函数”两种实现方式,也同样可以函数重载

  • 成员函数实现加号运算符重载
class Person {
public:
	Person(string name, int age) {
		m_name = name;
		m_age = age;
	}
	Person operator+(Person& p) {
		m_age = m_age + p.m_age;
		return *this;
	}
	string m_name;
	int m_age;
};
void test() {
	Person p1("Bob", 18);
	Person p2("Bob", 19);
	Person p3 = p1 + p2;
	cout << p3.m_age << endl;
}
  • 全局函数实现加号运算符重载
Person operator+(Person& p1, Person& p2) {
	p1.m_age = p1.m_age + p2.m_age;
	return p1;
}

void test() {
	Person p1("Bob", 18);
	Person p2("Bob", 19);
	Person p3 = p1 + p2;
	cout << p3.m_age << endl;
}
  • operator+()发生函数重载
Person operator+(Person& p1, Person& p2) {		// 传入参数为2个Person类的引用
	p1.m_age = p1.m_age + p2.m_age;
	return p1;
}

Person operator+(Person& p, int AddAge) {		// 传入参数为1个Person类的引用与1个int型
	p.m_age = p.m_age + AddAge;
	return p;
}

void test() {
	Person p1("Bob", 18);
	Person p2("Bob", 19);
	Person p3 = p1 + p2;
	Person p4 = p2 + 10;
	cout << p3.m_age << endl;
	cout << p4.m_age << endl;
}
11.5.2 左移运算符重载

作用:解决无法使用cout << 输出一个自建数据类型

特点:只能在全局函数实现,类内方式会导致cout与p的顺序出错

语法:operator<<()

class Person {
	friend ostream& operator<<(ostream& os, Person& p);	// 由于函数需要访问私有成员,故做友元声明
public:
	Person(string name,int age):m_name(name),m_age(age){}
private:
	string m_name;
	int m_age;
};

ostream& operator<<(ostream& os, Person& p) {		// 左移运算符重载 函数
	os << p.m_name << " " << p.m_age;				// 做自建数据类型的输出定义
	return os;
}

void test() {
	Person p("Bob", 18);
	cout << p << endl;
}
  1. 为什么有两个参数,且顺序为os p
    • 左移运算符<<是二元运算符,若只使用参数p,无法指定数据输出到哪个流
    • 因为编译器在调用cout << p时,会自动把左操作数cout作为operator<<()的第一个参数,右操作数p作为第二个参数,如果顺序颠倒,则传入的参数类型与实际不符,报错
  2. 为什么形参要使用引入的方式传入?
    • Person& 的传入方式在之前已经出现过,主要是为了避免不必要的拷贝,提高代码效率,由于该函数并没有对对象的内容进行修改,所以此处的引用传递不是必须的,但是建议这么写
    • ostream是标准库中的一个表示输出流的类,由于std::ostream类是不可拷贝的,其拷贝构造函数是被=delete的,所以当使用值传递时,系统会被删除调用拷贝构造函数引发错误,故必须使用引用传递
  3. 为什么要返回,且返回的是形参os?
    • 需要返回 是类比于this指针一节的“链式传递”,当不加返回值时,无法做到cout << p << endl
    • operator<<()结合两个入口参数osp,可在使用时简化为os << p,当需要进行链式操作时,返回值应该仍可作为<<符号的左值,故返回的是引用值os
11.5.3 递增运算符重载

作用:解决无法使用++自增一个自建数据类型

语法:前置递增 operator++() 后置递增operator++(int)

class Person {
	friend ostream& operator<<(ostream& cout, Person& p);		// 友元函数声明
public:
	Person(string name,int age):m_name(name),m_age(age){}		// 构造函数
	Person& operator++() {										// 前置递运算符重载
		++this->m_age;
		return *this;
	}
	Person& operator++(int) {									// 后置递增运算符重载
		Person temp = *this;
		this->m_age++;
		return temp;
	}
private:
	string m_name;
	int m_age;
};

ostream& operator<<(ostream& os, Person& p) {					// 左移运算符重载
	os << p.m_name << p.m_age;
	return os;
}

//void test1() {												// 前置递增测试
//	Person p("Bob", 18);
//	cout << ++(++p) << endl;
//	cout << p << endl;
//}

void test2() {													// 后置递增测试
	Person p("Bob", 18);
	cout << p++ << endl;
	cout << p << endl;
}

前置递增运算符与后置递增运算符重载的区别:

  • 前置无需传参,后置需要传单int,编译器会根据形参情况自动确定前置与后置

  • 前置返回当前对象,后置返回临时对象

递减运算符重载同理

11.5.4 赋值运算符重载

作用:解决拥有在堆区开辟属性的类的对象浅拷贝问题

语法:operator=()

知识补充:C++编译器给一个类至少添加4个函数:

  • 默认构造函数

  • 默认析构函数

  • 默认拷贝构造函数

  • 赋值运算符重载函数operator=()

class Person {
	friend void test();
public:
	Person(int age) {				// 构造函数 在堆区开辟空间存储“年龄”的值,并用指针m_age维护
		m_age = new int(age);
	}
	~Person() {						// 析构函数 清理在堆区开辟的空间
		if (this->m_age != NULL) {
			delete this->m_age;
			this->m_age = NULL;
		}
	}
	Person& operator=(Person p) {
		// this->m_age = p.m_age;	
		if (this->m_age != NULL) {			// 在赋值前先清理在堆区开辟的属性
			delete this->m_age;
			this->m_age = NULL;
		}
		this->m_age = new int(*p.m_age);	// 深拷贝
		return *this;						// 可以链式运算
	}
private:
	string m_name;
	int* m_age;
};

void test() {
	Person p1(10);
	Person p2(20);
	Person p3(30);
	p3 = p2 = p1;		// 从右至左的链式赋值
	cout << "p1的年龄为:" << *p1.m_age << endl;
	cout << "p2的年龄为:" << *p2.m_age << endl;
	cout << "p3的年龄为:" << *p3.m_age << endl;
}

若使用14行的p.m_age = this->m_age,会导致this->m_agep.m_age指向同一片堆区内存,在析构时令同一个地址被释放两次。

11.5.5 关系运算符重载

作用:解决关系运算符无法对自建数据类型进行判定的问题,关系运算符包括== != >= <= > <

语法:operator==()

class Person {
public:
	Person(string name,int age):m_name(name),m_age(age){}
	bool operator==(Person p) {
		if (this->m_name == p.m_name && this->m_age == p.m_age) {
			return true;
		}
		else
			return false;
	}
private:
	string m_name;
	int m_age;
};

void test() {
	Person p1("Bob",10);
	Person p2("Bob",20);
	if (p1 == p2) {
		cout << "p1与p2相等" << endl;
	}
	else {
		cout << "p1与p2不相等" << endl;
	}
}
11.5.6 函数调用运算符重载

作用:用于重载函数调用运算符(),重载后称为仿函数

语法:operator()()

特点:仿函数的写法不固定,体现在第二个括号中的传参可根据需求任意选择

class MyPrint {
public:
	void operator()(string str) {
		cout << str << endl;
	}
};

void test() {
	MyPrint myprint;
	myprint("Hello World");
}

匿名对象:类名+()成为匿名对象

匿名函数对象:9-10行的操作可用MyPrint()("Good Morning")来代替,称为匿名函数对象

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值