C++类和对象(五):运算符重载


0 运算符重载

对于内置数据类型,编译器了解运算规则;对于自定义数据类型,编译器不了解运算规则

运算符重载重新定义已有运算符,赋予其它新功能,以适应不同数据类型。


1 加法运算符重载(+)

作用:实现自定义数据类型的加法运算。
用法:通过实现全局函数(2个参数)成员函数(1个参数)重载加法运算符,即重载编译器的默认函数operateor+

注1:在重载加法运算符前,使用自定义数据类型作为加号的操作数时,编译器报错:没有与这些操作数匹配的“+”运算符
注2:运算符重载可发生函数重载,复用函数名。
注3:对于内置数据类型,其表达式的运算符不可改变;不能滥用运算符重载。

示例:全局函数和成员函数实现加法运算符重载

#include <iostream>
using namespace std;

class Object {
    
    
public:
	int fieldA;
	int fieldB;

	Object() {
    
    }

	Object(int a, int b) {
    
    
		this->fieldA = a;
		this->fieldB = b;
	}

	/* 成员函数实现加法运算符重载 */
	//Object operator+(Object &obj) {
    
    
	//	Object temp;
	//	temp.fieldA = this->fieldA + obj.fieldA;
	//	temp.fieldB = this->fieldB + obj.fieldB;
	//	
	//	return temp;
	//}
};

/* 全局函数实现加法运算符重载 */
Object operator+(Object& p, Object& q) {
    
    
	Object temp;
	temp.fieldA = p.fieldA + q.fieldA;
	temp.fieldB = p.fieldB + q.fieldB;
	return temp;
}

//全局函数的函数重载
Object operator+(Object& p, int num) {
    
    
	Object temp;
	temp.fieldA = p.fieldA + num;
	temp.fieldB = p.fieldB + num;
	return temp;
}

int main() {
    
    
	Object m;
	m.fieldA = 1;
	m.fieldB = 2;

	Object n;
	n.fieldA = 10;
	n.fieldB = 20;

	/* 调用成员函数重载加法运算符 */
	//Object obj1 = m + n;			//等价于m.operator+(n);
	//cout << obj1.fieldA << endl;	//11
	//cout << obj1.fieldB << endl;	//22

	/* 调用全局函数重载加法运算符 */
	Object obj2 = m + n;			//等价于operator+(m, n);
	cout << obj2.fieldA << endl;	//11
	cout << obj2.fieldB << endl;	//22

	//全局函数的函数重载
	int num = 100;
	Object obj3 = m + num;			//等价于operator+(m, 100);
	cout << obj3.fieldA << endl;	//101
	cout << obj3.fieldB << endl;	//102

	return 0;
}

2 左移运算符重载(<<)

作用重载左移运算符结合友元,可实现输出自定义数据类型
用法:通过实现全局函数(cout和对象等2个参数)重载左移运算符,即重载编译器的默认函数operateor<<;将全局函数作为友元,实现对类私有成员的访问。

注1:不能通过成员函数重载左移运算符,对象调用成员函数时obj.operator<<(cout)相当于obj << cout,此时,标准输出流对象cout左移运算符<<右侧,不满足要求。
注2:标准输出流对象cout属于ostream类,需保证全局仅存在唯一的个cout对象,并使用引用的方式接收。
注3:为保证cout << ... << ... << endl;链式编程风格,全局函数的返回类型应为ostream类型

示例:全局函数和友元实现左移运算符重载

#include <iostream>
using namespace std;

class Object {
    
    
	/* 全局函数作友元 */
	friend ostream& operator<<(ostream &cout, Object &obj);

private:
	int fieldA;
	int fieldB;

public:
	Object() {
    
    }

	Object(int a, int b) {
    
    
		this->fieldA = a;
		this->fieldB = b;
	}
};

/* 全局函数实现左移运算符重载;设置友元访问类的私有成员 */
//cout对象全局唯一,全局函数以引用类型返回
//为实现链式编程,全局函数的返回类型应为ostream类型
ostream& operator<<(ostream &cout, Object &obj) {
    
    
	cout << "fieldA = " << obj.fieldA << endl;
	cout << "fieldB = " << obj.fieldB << endl;
	return cout;
}

int main() {
    
    
	Object o(1, 2);
	cout << o << "调用重载的左移运算符" << endl;	//链式编程
	/*
	//输出结果
	fieldA = 1
	fieldB = 2
	调用重载的左移运算符
	*/

	return 0;
}

3 递增运算符重载(++)

作用:可实现自定义的整型数据
用法:通过实现成员函数(无参数)重载递增运算符,即重载编译器的默认函数operateor++前置递增的成员函数返回对象的引用类型后置递增的成员函数返回值(对象的拷贝)

注1:内置整型变量的递增运算符分为前置递增后置递增,实现自定义数据类型的递增运算符时,同名成员函数使用占位参数相区分并重载。
注2:
前置递增函数:*this表示当前对象,函数返回类型为引用(别名,即同一个对象,而不是对象的拷贝),否则连续递增时出错。
后置递增函数:*this表示当前对象,函数返回类型为值(对象的拷贝);创建临时对象记录当前对象,先执行递增操作再返回临时对象;临时变量会在函数调用完毕后释放,若函数返回类型为引用(别名,即同一个对象),再次访问时非法(即访问已释放的内存)。

示例:左移运算符重载和成员函数实现递增运算符重载

#include <iostream>
using namespace std;

class MyInteger {
    
    
	/* 全局函数作友元 */
	friend ostream& operator<<(ostream& cout, MyInteger myint);

private:
	int num;

public:
	//默认无参构造:初始化为0
	MyInteger() {
    
    	
		num = 0;
	}

	//带参构造:初始化为指定值
	MyInteger(int number) {
    
    
		this->num = number;
	}

	/* 前置递增函数 */
	//解引用*this表示当前对象,返回类型为引用(否则连续递增时出错)
	MyInteger & operator++() {
    
    
		this->num++;	//等价于 num++;
		return *this;
	}

	/* 后置递增函数 */
	//使用int作为占位参数,与前置递增相区分并重载
	//解引用*this表示当前对象,返回类型为值(对象)
	//创建临时变量并记录当前对象,先递增再返回已记录的临时变量
	//临时变量会在函数调用完毕后释放,若函数返回引用(别名,同一个对象),再次访问时非法
	MyInteger operator++(int) {
    
    
		MyInteger cur = *this;	//创建临时变量cur,记录当前对象*this
		this->num++;	//等价于 num++;
		return cur;		//返回临时变量(临时变量在函数调用完毕后释放)
	}
};

/* 全局函数实现左移运算符重载;设置友元访问类的私有成员 */
ostream& operator<<(ostream& cout, MyInteger myint) {
    
    
	cout << myint.num;
	return cout;
}

int main() {
    
    
	//测试:前置递增++a
	MyInteger a(10);
	cout << ++a << endl;	//11
	cout << a << endl;		//11

	//测试:后置递增b++
	MyInteger b(20);
	cout << b++ << endl;	//20
	cout << b << endl;		//21

	return 0;
}

4 赋值运算符重载(=)

作用:使用深拷贝实现自定义数据类型赋值及连续赋值操作。
用法:通过实现成员函数(对象等1个参数)重载赋值运算符,即重载编译器的默认函数operateor=,使用深拷贝完成自定义数据类型的赋值操作,且函数返回类型为对象的引用类型,支持类似内置数据类型的连续赋值操作,如c = b = a

创建一个类时,C++编译器默认会对类添加至少4个函数
(1)默认构造函数(无参,函数体为空,即空实现)
(2)默认析构函数(无参,函数体为空,即空实现)
(3)默认拷贝构造函数(对属性进行值拷贝
(4)赋值运算符operator=函数(对属性进行值拷贝

注:若类中有属性指向堆区,则赋值操作时存在浅拷贝问题,调用析构函数释放堆区内存时,可能导致同一块内存被重复释放从而程序崩溃,,需使用深拷贝解决。

示例:深拷贝和成员函数实现赋值运算符重载

#include <iostream>
using namespace std;

class Object {
    
    
public:
	int* pField;

	Object() {
    
    }

	//自定义构造函数:在堆区初始化
	Object(int num) {
    
    
		pField = new int(num);	//返回堆区内存地址
	}

	//自定义析构函数
	~Object() {
    
    
		if (pField != NULL) {
    
    
			delete pField;
			pField = NULL;
		}
	}

	/* 成员函数实现赋值运算符重载 */
	//使用深拷贝实现堆区成员赋值
	//先判断堆区成员是否为空:非空则释放后再深拷贝
	//成员函数返回引用类型,支持连续赋值
	Object& operator=(Object &obj) {
    
    
		//编译器默认的赋值运算符重载是属性浅拷贝(地址值拷贝,指向同一个堆区对象)
		//this->pField = obj.pField;
		
		//先判断堆区成员是否为空:非空则释放后再深拷贝
		if (pField != NULL) {
    
    
			delete pField;
			pField = NULL;
		}

		//深拷贝赋值,在堆区新开辟内存
		pField = new int(*obj.pField);	//通过解引用获取值

		//返回当前对象,实现连续赋值
		return *this;
	}
};

int main() {
    
    
	Object a(1);
	Object b(2);
	Object c(3);

	cout << "*a.pField =" << *a.pField << endl;		//1
	cout << "*b.pField =" << *b.pField << endl;		//2
	cout << "*c.pField =" << *c.pField << endl;		//3

	//自定义数据类型的连续赋值
	c = b = a;

	cout << "*a.pField =" << *a.pField << endl;		//1
	cout << "*b.pField =" << *b.pField << endl;		//1
	cout << "*c.pField =" << *c.pField << endl;		//1

	return 0;
}

5 关系运算符重载(==、!=)

作用:比较自定义数据类型的对象。
用法:通过实现成员函数(对象等1个参数)重载关系运算符,即重载编译器的默认函数operateor==operateor!=

示例:成员函数实现关系运算符重载

#include <iostream>
using namespace std;

class Object {
    
    
public:
	int fieldA;
	string fieldB;

	Object() {
    
    }

	Object(int num, string str) {
    
    
		this->fieldA = num;
		this->fieldB = str;
	}

	/* 关系运算符重载 */
	bool operator==(Object& obj) {
    
    
		return (this->fieldA == obj.fieldA) && (this->fieldB == obj.fieldB);
	}

	bool operator!=(Object& obj) {
    
    
		return (this->fieldA != obj.fieldA) || (this->fieldB != obj.fieldB);
	}
};

int main() {
    
    
	Object o1(18, "Tom");
	Object o2(18, "Jerry");

	if (o1 == o2) {
    
    
		cout << "自定义数据类型相等" << endl;
	}
	else if(o1 != o2) {
    
    
		cout << "自定义数据类型不相等" << endl;
	}

	return 0;
}

6 函数调用运算符重载(())及仿函数

函数调用运算符小括号()可发生重载,使用方式类似于函数调用,称为仿函数

注1:仿函数无固定写法,使用灵活。
注2:可通过匿名对象使用函数调用运算符,如类名()(),即匿名函数对象

用法:通过实现成员函数重载函数调用运算符,即重载编译器的默认函数operateor(),并通过创建类的对象进行调用,如对象();

示例:成员函数实现函数调用运算符重载

#include <iostream>
using namespace std;

class Sum {
    
    
public:
	/* 仿函数使用灵活,无固定写法 */
	//成员函数1:函数形参只包含1个时,直接打印
	void operator()(int num) {
    
    
		cout << "结果为:" << num << endl;
	}

	//成员函数2:函数形参包含2个时,求和
	int operator()(int num1, int num2) {
    
    
		return num1 + num2;
	}

	//成员函数3:函数形参包含3个时,求和
	int operator()(int num1, int num2, int num3) {
    
    
		return num1 + num2 + num3;
	}
};

int main() {
    
    
	/* 1.通过对象调用 */
	Sum sum;
	sum(1);		//1				//调用成员函数1

	int res1 = sum(1, 2);		//调用成员函数2
	sum(res1);	//3				//调用成员函数1

	int res2 = sum(1, 2, 3);	//调用成员函数2
	sum(res2);	//6				//调用成员函数1

	/* 2.通过匿名对象调用-匿名函数对象 */
	//类名()为匿名对象,第二个小括号为函数调用运算符重载
	Sum()(1);	//1				//调用成员函数1

	return 0;
}

7 指针运算符重载(指针指向->、解引用*)

背景:在堆区创建对象并使用完毕后,需显式使用delete操作符调用析构函数从而释放对象,否则会导致内存泄露

注:对象指针调用成员函数p->func()等价于对象调用成员函数(*p).func()

作用:利用智能指针托管new操作符创建的堆区对象,无需手动使用delete操作符释放堆区内存。
用法
(1)模拟智能指针类,使用装饰者模式包装目标类,通过在栈区创建与释放智能指针类对象,实现堆区目标类对象的自动释放。即栈区智能指针类对象释放并调用析构函数时,会内在地调用堆区目标类对象的析构函数。
(2)通过实现成员函数重载指针相关的运算符,如指针指向运算符 ->解引用运算符 *,即重载编译器的默认函数operateor->operateor*

注:重载指针指向运算符 ->时,通过智能指针类对象返回指向目标类对象的指针,即smartPointer->返回pObj,则对象指针调用成员函数pObj->func()等价于smartPointer->->func(),编译器内部会将2个指针指向运算符->->简化为1个->

注:智能指针:使用类模板托管new操作符创建的堆区对象,避免堆区内存泄露。
C++98auto_ptr<Object> 指针变量名(new Object);
C++11unique_ptr<Object> 指针变量名(new Object);,需包含头文件#include <memory>

示例:模拟智能指针类重载指针运算符,托管堆区对象

#include <iostream>
using namespace std;

/* 目标类 */
class Object {
    
    
private:
	int field;

public:
	//构造函数
	Object() {
    
    
		cout << "Object目标类的默认无参构造函数被调用" << endl;
	}

	Object(int num) {
    
    
		this->field = num;
		cout << "Object目标类的有参构造函数被调用" << endl;
	}

	//析构函数
	~Object() {
    
    
		cout << "Object目标类的析构函数被调用" << endl;
	}

	//成员函数:显示私有成员属性
	void func() {
    
    
		cout << "私有成员field = " << this->field << endl;
	}
};

/* 智能指针类:包装并托管目标类的堆区对象 */
class SmartPointer {
    
    
private:
	Object* pObj;

public:
	//构造函数
	SmartPointer(Object *p) {
    
    
		this->pObj = p;
		cout << "SmartPointer智能指针类的有参构造函数被调用" << endl;
	}

	//析构函数:自动调用目标类Object的析构函数
	~SmartPointer() {
    
    
		cout << "SmartPointer智能指针类的析构函数被调用" << endl;

		//调用目标类Object的析构函数
		if (this->pObj != NULL) {
    
    
			delete pObj;
			pObj = NULL;
		}
	}

	//成员函数:重载指针指向运算符->
	//返回目标类指针类型
	Object* operator->() {
    
    
		return this->pObj;
	}

	//成员函数:重载解引用运算符*
	//返回目标类对象引用类型,减少拷贝构造函数的开销
	Object& operator*() {
    
    
		return *(this->pObj);
	}
};

int main(){
    
    
	/*
	//在堆区创建目标类的对象
	Object* pObj = new Object(10);
	pObj->func();
	(*pObj).func();
	//delete pObj;	 //若未显式释放,则会导致堆区内存泄露
	*/

	//在堆区创建目标类的匿名对象,并将堆区地址(对象指针)传递给栈区的智能指针类对象
	//栈区的智能指针类对象在调用完毕后释放,会自动调用目标类的析构函数,释放堆区内存,无需显式delete
	SmartPointer sp(new Object(10));
	//sp->返回pObj,pObj->func()等价于sp->->func(),编译器内部会将 ->-> 简化为 ->。
	sp->func();		//本质为sp->->func();
	(*sp).func();

	return 0;
}

输出

/*
Object目标类的有参构造函数被调用
私有成员field = 10
私有成员field = 10
*/

Object目标类的有参构造函数被调用
SmartPointer智能指针类的有参构造函数被调用
私有成员field = 10
私有成员field = 10
SmartPointer智能指针类的析构函数被调用
Object目标类的析构函数被调用

8 不可重载逻辑与(&&)、逻辑或(||)运算符

逻辑与逻辑非运算符具有短路特性,不可重载逻辑与(&&)(operator&&函数)、逻辑或(||)运算符(operator||函数)。


9 运算符重载总结

(1)<<>>只能通过全局函数配合友元函数进行重载;
(2)=[]()->只能通过成员函数进行重载;
(3)不可重载逻辑与&&、逻辑或||运算符,可能无法实现短路特性

注:左移运算符<<和右移运算符>>通过全局函数重载,其余运算符均通过成员函数重载。

使用建议

运算符 用法
全部一元运算符 成员函数
=[]()->->* 必须成员函数
+=-=/=*=^=&=!=%=>>=<<= 成员函数
其它二元运算符 非成员函数

10 运算符重载练习:C++字符串类封装

C++中提供字符串类string,支持等号赋值=加法运算+追加函数append()等操作,方便字符串操作,其底层维护C语言的字符数组,实现对字符数组的各类操作。

通过字符数组实现字符串拷贝的步骤:
(1)在堆区开辟新的内存空间,存储原字符串所有字符’和'\0'结束符,并返回指向堆区字符数组的指针
字符串长度:通过strlen函数根据字符数组指针,获取字符串长度(不含'\0'结束符)。
char *dest = new char[strlen(src) + 1];
strlen函数原型unsigned int strlen(const char *pStr);

(2)字符串拷贝:通过strcpy函数将含有'\0'结束符源字符串复制新开辟的内存空间,返回类型为char*
strcpy(dest, src);
strcpy函数原型char* strcpy(char* dest, const char* src);

字符串操作的其它相关函数:
(3)字符串比较strcmp函数:str1=str2返回0;str1<str2返回负数;str1>str2返回正数。
strcmp函数原型int strcmp(const char *str1, const char *str2)

(4)字符串拼接strcat函数:将src源字符串(包括结束符“\0”)复制到dest目标字符串后(删除dest末尾的结束符“\0”)。
strcat函数原型extern char* strcat(char *dest, const char *src);

注1:需保证*dest目标字符串的内存空间足够大,可容纳被复制进来的*src源字符串。
注2:src源字符串中的原有字符不变(限定为只读状态),返回指向dest目标字符串的指针。

(5)内存初始化memset函数:将某块内存中的内容全部设置为指定值。将指针或数组对应的内存空间,全部初始化为指定值ch(指针或数组的内存大小为count)。
memset函数原型extern void* memset(void *buffer, int ch, int count);


练习:通过运算符重载模拟字符串类MyString类。
(1)成员属性
①维护堆区字符数组的指针:char* pStr;
②字符串长度:int length;

(2)成员函数
①无参构造函数
②有参构造函数
③拷贝构造函数
④析构函数
⑤成员函数-重载等号赋值运算符=:使用深拷贝解决浅拷贝问题
⑥成员函数-重载索引中括号[]:访问或修改指定索引位置的字符
⑦成员函数-重载加法运算符+:字符串拼接
⑧成员函数-重载关系运算符==:比较自定义的数据类型(自定义字符串类)

(3)全局函数(配合友元,访问类的私有成员)
①重载左移运算符<<:(连续)输出自定义的数据类型(自定义字符串类)
②重载右移运算符>>:(连续)输入自定义的数据类型(自定义字符串类)


MyString类头文件:myString.h

#pragma once //防止头文件被重复包含
#include <iostream>
using namespace std;

/* 模拟字符串类 */
//头文件包含:成员属性、成员函数的声明
class MyString {
    
    
	//全局函数作友元,访问类的私有成员
	friend ostream& operator<<(ostream& cout, MyString& str);
	friend istream& operator>>(istream& cout, MyString& str);

private:
	char* pStr;	//底层维护指向堆区开辟的字符数组的指针
	int length;		//字符串长度(不计算\0)

public:
	//无参构造函数
	//MyString str;
	MyString();

	//有参构造函数
	//MyString str = "Hello";
	MyString(const char* src);

	//拷贝构造函数
	//MyString str2(str); 或 MyString str2 = str;
	MyString(const MyString& str);

	//析构函数
	~MyString();

	//成员函数:重载等号赋值运算符,支持连续赋值
	MyString& operator=(const char* str);
	MyString& operator=(const MyString &str);

	//成员函数:重载[],支持索引访问
	char& operator[](int pos);

	//成员函数:重载加法运算符,支持自定义数据类型相加
	//MyString str3 = str1 + str2;
	MyString operator+(const MyString& str);
	//MyString str3 = str1 + "abc";
	MyString operator+(const char* str);

	//成员函数:重载关系运算符==,比较自定义数据类型
	bool operator==(const MyString& str);
	bool operator==(const char* str);
};

MyString类源文件:myString.cpp

#define _CRT_SECURE_NO_WARNINGS	//屏蔽strcpy函数的不安全告警
#include "myString.h"

//全局函数:重载左移<<运算符,配合友元访问类中私有成员
ostream& operator<<(ostream& cout, MyString& str) {
    
    
	cout << str.pStr;	//全局函数作友元,访问私有成员
	return cout;		//返回全局唯一的cout对象
}

//全局函数:重载右移>>运算符,配合友元访问类中私有成员
istream& operator>>(istream& cout, MyString& str) {
    
    
	//先判断堆区是否存在数据,若有需先清空
	if (str.pStr != NULL) {
    
    	//全局函数作友元,访问私有成员
		delete[] str.pStr;
		str.pStr = NULL;
	}

	//将输入数据先存入栈区的字符数组(缓冲区),再拷贝至堆区
	char buffer[1024] = {
    
     0 };
	cin >> buffer;
	str.pStr = new char[strlen(buffer) + 1];
	strcpy(str.pStr, buffer);
	str.length = strlen(buffer);

	return cin;		//返回全局唯一的cin对象
}

//无参构造函数(形参:空参)
MyString::MyString()
{
    
    
	//cout << "调用MyString类的无参构造函数" << endl;
	//在堆区新开辟字符数组,数组长度为strlen(str) + 1
	this->pStr = new char[1];
	//strcpy函数实现字符串拷贝
	strcpy(this->pStr, "");
	//成员属性赋值:字符串长度
	this->length = 0;
}

//有参构造函数(形参:字符数组指针)
MyString::MyString(const char* src)
{
    
    
	//cout << "调用MyString类的有参构造函数" << endl;
	//在堆区新开辟字符数组,数组长度为strlen(str) + 1
	this->pStr = new char[strlen(src) + 1];
	//strcpy函数实现字符串拷贝
	strcpy(this->pStr, src);
	//成员属性赋值:字符串长度
	this->length = strlen(src);
}

//拷贝构造函数(形参:对象)
MyString::MyString(const MyString& str)
{
    
    
	//cout << "调用MyString类的拷贝构造函数" << endl;
	//在堆区新开辟字符数组,数组长度为strlen(str) + 1
	this->pStr = new char[strlen(str.pStr) + 1];
	//strcpy函数实现字符串拷贝
	strcpy(this->pStr, str.pStr);
	//成员属性赋值:字符串长度
	this->length = strlen(str.pStr);
}

//析构函数
MyString::~MyString()
{
    
    
	if (this->pStr != NULL) {
    
    
		//cout << "调用MyString类的析构函数" << endl;
		delete[] this->pStr;	//释放字符数组的指针
		this->pStr = NULL;
	}
}

//成员函数:重载等号赋值运算符,支持连续赋值【情况1】
MyString& MyString::operator=(const char* str)
{
    
    
	//先判断当前字符数组是否为空
	if (this->pStr != NULL) {
    
    
		delete[] this->pStr;
		this->pStr = NULL;
	}

	//深拷贝赋值
	this->pStr = new char[strlen(str) + 1];
	strcpy(this->pStr, str);
	this->length = strlen(str);

	//返回当前对象,支持连续等号赋值
	return *this;
}

//成员函数:重载等号赋值运算符,支持连续赋值【情况2】
MyString& MyString::operator=(const MyString& str)
{
    
    
	//先判断当前字符数组是否为空
	if (this->pStr != NULL) {
    
    
		delete[] this->pStr;
		this->pStr = NULL;
	}

	//深拷贝赋值
	this->pStr = new char[strlen(str.pStr) + 1];
	strcpy(this->pStr, str.pStr);
	this->length = strlen(str.pStr);

	return *this;
}

//成员函数:重载索引访问([])
char& MyString::operator[](int pos)
{
    
    
	//返回当前字符数组指定索引上的字符
	return this->pStr[pos];
}

//成员函数:重载加法运算符,支持自定义数据类型相加【情况1】
MyString MyString::operator+(const MyString& str)
{
    
    
	//计算字符串拼接后,所需堆区内存空间
	int size = this->length + strlen(str.pStr) + 1;
	char* temp = new char[size];
	//将堆区内存清零(初始化)
	memset(temp, 0, size);

	//依次拼接字符串
	strcat(temp, this->pStr);
	strcat(temp, str.pStr);

	//根据堆区字符数组,创建MyString类对象
	MyString newStr(temp);
	//释放堆区字符数组,避免堆区内存泄露
	delete[] temp;

	//返回MyString类对象
	return newStr;
}

//成员函数:重载加法运算符,支持自定义数据类型相加【情况2】
MyString MyString::operator+(const char* str)
{
    
    
	//计算字符串拼接后,所需堆区内存空间
	int size = this->length + strlen(str) + 1;
	char* temp = new char[size];
	//将堆区内存清零(初始化)
	memset(temp, 0, size);

	//依次拼接字符串
	strcat(temp, this->pStr);
	strcat(temp, str);

	//根据堆区字符数组,创建MyString类对象
	MyString newStr(temp);
	//释放堆区字符数组,避免堆区内存泄露
	delete[] temp;

	//返回MyString类对象
	return newStr;
}

//成员函数:重载关系运算符==,比较自定义数据类型【情况1】
bool MyString::operator==(const MyString& str)
{
    
    
	return (strcmp(this->pStr, str.pStr) == 0) ? true : false;
}

//成员函数:重载关系运算符==,比较自定义数据类型【情况2】
bool MyString::operator==(const char* str)
{
    
    
	return (strcmp(this->pStr, str) == 0) ? true : false;
}

测试程序:main.cpp

#define _CRT_SECURE_NO_WARNINGS	//屏蔽strcpy函数的不安全告警

#include <iostream>
using namespace std;
#include "myString.h"

//重载左移运算符测试
void func1() {
    
    
	MyString str1 = "Hello";
	MyString str2 = str1;
	//需重载左移运算符,输出自定义字符串MyString对象
	cout << "str1 = " << str1 << endl;
	cout << "str2 = " << str2 << endl;
}

//重载右移运算符测试
void func2() {
    
    
	/*
	char buff[64] = {0};
	char buff2[64] = {0};

	//cin支持链式编程
	cin >> buff >> buff2;
	cout << buff << endl;
	cout << buff2 << endl;
	*/

	MyString str = "Hello";
	cout << "对str重新赋值" << endl;
	cin >> str;
	cout << "新赋值后的str = " << str << endl;
}

//重载等号赋值运算符测试
void func3() {
    
    
	MyString str1;
	MyString str2;

	str1 = str2 = "abc";
	cout << "str1 = " << str1 << endl;
	cout << "str2 = " << str2 << endl;
}

//重载索引访问([])测试
void func4() {
    
    
	MyString str = "abc";
	cout << "str[1] = " << str[1] << endl;

	cout << "修改前的str = " << str << endl;
	str[1] = 'a';
	cout << "修改后的str = " << str << endl;
}

//重载加法运算符测试
void func5() {
    
    
	MyString str1 = "abc";
	MyString str2 = "def";

	MyString str3 = str1 + str2;
	cout << "拼接后的str3 = " << str3 << endl;
	
	MyString str4 = str1 + "def";
	cout << "拼接后的str4 = " << str4 << endl;
}

//重载关系运算符==测试
void func6() {
    
    
	MyString str1 = "hellow";
	MyString str2 = "hello";

	cout << "str1 == str2:" << (str1 == str2) << endl;
	cout << "str1 == \"hello\":" << (str1 == "hello") << endl;
}

int main() {
    
    
	//func1();
	//func2();
	//func3();
	//func4();
	//func5();
	func6();

	return 0;
}

猜你喜欢

转载自blog.csdn.net/newson92/article/details/113661805
今日推荐