文章目录
- 一、重载 等号 = 运算符
- 1、等号 = 运算符 与 拷贝构造函数
- 2、重载 等号 = 运算符 - 右操作数为 String 对象
- 3、不同的右操作数对应的 重载运算符函数
 
- 二、重载 下标 [] 运算符
- 三、完整代码示例
- 1、String.h 类头文件
- 2、String.cpp 类实现
- 3、Test.cpp 测试类
- 4、执行结果
 
一、重载 等号 = 运算符
1、等号 = 运算符 与 拷贝构造函数
等号 = 操作符 的 作用是 使用一个现有对象 为 另外一个现有对象赋值 ;
- 注意与 拷贝构造函数 区分 , 拷贝构造函数是 使用一个先有对象 为 一个新的对象进行初始化 ;
- 下面的代码中 , 分别调用 等号操作符 和 拷贝构造函数 ;
String s1, s2;
s1 = s2;		// 这是使用 等号操作符 进行运算
String s3 = s2; // 这是使用 拷贝构造函数
2、重载 等号 = 运算符 - 右操作数为 String 对象
使用 成员函数 实现 重载 等号 = 运算符 :
- 首先 , 写出函数名 , 函数名规则为 " operate " 后面跟上要重载的运算符 , 
  -  
        
         
          
          
            2 
           
          
         
           2 
          
         
       2 个对象 Student s1, s2之间进行 等号运算 , 使用一个现有对象 为 另外一个现有对象赋值 ;
- 使用时用法为 s1 = s2;
- 函数名是 operator=;
 
-  
        
         
          
          
            2 
           
          
         
           2 
          
         
       2 个对象 
operator=
- 然后 , 根据操作数 写出函数参数 , 参数一般都是 对象的引用 ;  
  - 等号运算符 使用时用法为 s1 = s2;
- 左操作数 : 其中 左操作数 是 s1 , 这里通过 this 指针调用 , 不需要声明在参数中 ;
- 右操作数 - 情况 ① : 右操作数 也是 String 对象 ; 该操作数需要声明在参数中 , 注意 普通数据类型 直接声明 , 对象数据类型 需要声明 为 引用类型 ;
- 上述两个是对象类型 , 对象一般传入 指针 或 引用 , 由于是基础数据类型 , 这里传入基础数据类型 ; 如果是 对象类型 , 则传入引用 ;
- 右操作数 - 情况 ② : 还有一种情况是 s1 = "Tom"的用法 , 这样 右操作数 是 char* 类型的字符串 ;
 
- 等号运算符 使用时用法为 
operator=(const String& s)	// 传入 String 对象 , 使用 const 修饰参数 , 防止传入的对象被修改
operator=(const char* p)	// 传入字符串值
- 再后 , 根据业务完善返回值 , 返回值可以是 引用 / 指针 / 元素 ;  
  - 此处返回值返回 String& 对象引用 , 可用于其它链式调用 ;
- 如果返回 String 对象 , 则是一次性的匿名对象 ;
 
String& operator=(const String& s)	// 传入 String 对象 , 使用 const 修饰参数 , 防止传入的对象被修改
String& operator=(const char* p)	// 传入字符串值
- 最后 , 实现函数体 , 编写具体的运算符操作业务逻辑 ;  
  - 先把本对象已分配的内存释放掉 ;
- 再进行赋值操作 ;
 
3、不同的右操作数对应的 重载运算符函数
不同的右操作数对应的 重载运算符函数 :
- 右操作数是 String 对象的情况 :
// 重载等号 = 操作符 , 右操作数是 String 对象的情况
String& String::operator=(const String& s)
{
	// 先处理本对象已分配的内存
	if (this->m_p != NULL)
	{
		// 之前使用 new 分配的内存
		// 释放内存就需要使用 delete 
		// 使用 malloc 分配的内存需要使用 free 释放
		delete[] this->m_p;
		// 设置指针指为空 , 避免出现野指针
		this->m_p = NULL;
		// 设置字符串长度为 0
		this->m_len = 0;
	}
	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = s.m_len;
	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->m_p = new char[this->m_len + 1];
	// 拷贝字符串到 m_p 指向的内存中
	strcpy(this->m_p, s.m_p);
	cout << "调用重载等号 = 操作符函数 String& String::operator=(const String& s)" << endl;
	return *this;
}
- 右操作数为 字符串指针 的情况 :
// 重载等号 = 操作符 , 右操作数是 字符串常量值 的情况
String& String::operator=(const char* p)
{
	// 先处理本对象已分配的内存
	if (this->m_p != NULL)
	{
		// 之前使用 new 分配的内存
		// 释放内存就需要使用 delete 
		// 使用 malloc 分配的内存需要使用 free 释放
		delete[] this->m_p;
		// 设置指针指为空 , 避免出现野指针
		this->m_p = NULL;
		// 设置字符串长度为 0
		this->m_len = 0;
	}
	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = strlen(p);
	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->m_p = new char[this->m_len + 1];
	// 拷贝字符串到 m_p 指向的内存中
	strcpy(this->m_p, p);
	cout << "调用重载等号 = 操作符函数 String& String::operator=(const char* p)" << endl;
	return *this;
}
二、重载 下标 [] 运算符
使用 成员函数 实现 重载 下标 [] 运算符 :
- 首先 , 写出函数名 , 函数名规则为 " operate " 后面跟上要重载的运算符 , 
  - 下标 [] 运算符 , 使用时用法为 s[10];
- 重载 下标 [] 运算符 函数名是 operator[];
 
- 下标 [] 运算符 , 使用时用法为 
operator[]
- 然后 , 根据操作数 写出函数参数 , 参数一般都是 对象的引用 ;  
  - 下标 运算符 使用时用法为 s[10];
- 左操作数 : 其中 左操作数 是 s 对象 , 这里通过 this 指针调用 , 不需要声明在参数中 ;
- 右操作数 : 右操作数 是 int 类型 索引值 ;
 
- 下标 运算符 使用时用法为 
operator[](int i)
- 再后 , 根据业务完善返回值 , 返回值可以是 引用 / 指针 / 元素 ;  
  - 此处返回值 是 char 类型的 , 返回具体字符串指定索引的 char 类型字符 ;
- char 字符是内存中的一个地址中的值 , 这里返回引用类型 ;
 
char& operator[](int i)
- 最后 , 实现函数体 , 编写具体的运算符操作业务逻辑 ;
// 重载 数组下标 [] 操作符
char& String::operator[](int i)
{
	// 直接返回对应 i 索引字符
	return this->m_p[i];
}
三、完整代码示例
1、String.h 类头文件
#pragma once
#include "iostream"
using namespace std;
class String
{
public:
	// 默认的无参构造函数
	String();
	// 有参构造函数 , 接收一个 char* 类型字符串指针
	String(const char* p);
	// 拷贝构造函数 , 使用 String 对象初始化 对象值
	String(const String& s);
	// 析构函数
	~String();
public:
	// 重载等号 = 操作符 , 右操作数是 String 对象的情况
	String& operator=(const String& s);
	// 重载等号 = 操作符 , 右操作数是 字符串常量值 的情况
	String& operator=(const char* p);
	// 重载 数组下标 [] 操作符
	char& operator[](int i);
private:
	// 字符串长度 , 不包括 '\0'
	// 内存占用空间大小 = 字符串长度 + 1
	int m_len;
	// 字符串指针, 指向堆内存中的字符串
	char* m_p;
};
2、String.cpp 类实现
// 使用 strcpy 函数报错
// error C4996: 'strcpy': This function or variable may be unsafe. 
// Consider using strcpy_s instead. 
// To disable deprecation, use _CRT_SECURE_NO_WARNINGS. 
// See online help for details.
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"
// 默认的无参构造函数
String::String()
{
	// 默认构造一个空字符串 , 字符串长度为 0 
	// 但是 , 字符串指针 指向的内存空间大小是 1 , 内容是 '\0'
	m_len = 0;
	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	m_p = new char[m_len + 1];
	// 拷贝空字符串到 m_p 指向的内存中
	strcpy(m_p, "");
	cout << "调用无参构造函数" << endl;
}
// 有参构造函数 , 接收一个 char* 类型字符串指针
String::String(const char* p)
{
	if (p == NULL)
	{
		// 默认构造一个空字符串 , 字符串长度为 0 
		// 但是 , 字符串指针 指向的内存空间大小是 1 , 内容是 '\0'
		this->m_len = 0;
		// 使用 new 关键字为 char* m_p; 指针分配内存
		// 对于基础数据类型 new 等同于 malloc
		this->m_p = new char[this->m_len + 1];
		// 拷贝空字符串到 m_p 指向的内存中
		strcpy(m_p, "");
	}
	else
	{
		// 获取传入字符串的长度
		// 但是 , 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
		this->m_len = strlen(p);
		// 使用 new 关键字为 char* m_p; 指针分配内存
		// 对于基础数据类型 new 等同于 malloc
		this->m_p = new char[this->m_len + 1];
		// 拷贝字符串到 m_p 指向的内存中
		strcpy(m_p, p);
	}
	cout << "调用有参构造函数" << endl;
};
// 拷贝构造函数 , 使用 String 对象初始化 对象值
String::String(const String& s)
{
	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = s.m_len;
	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->m_p = new char[this->m_len + 1];
	// 拷贝字符串到 m_p 指向的内存中
	strcpy(this->m_p, s.m_p);
	cout << "调用拷贝构造函数" << endl;
}
// 析构函数
String::~String()
{
	if (this->m_p != NULL)
	{
		// 之前使用 new 分配的内存
		// 释放内存就需要使用 delete 
		// 使用 malloc 分配的内存需要使用 free 释放
		delete[] this->m_p;
		// 设置指针指为空 , 避免出现野指针
		this->m_p = NULL;
		// 设置字符串长度为 0
		this->m_len = 0;
	}
}
// 重载等号 = 操作符 , 右操作数是 String 对象的情况
String& String::operator=(const String& s)
{
	// 先处理本对象已分配的内存
	if (this->m_p != NULL)
	{
		// 之前使用 new 分配的内存
		// 释放内存就需要使用 delete 
		// 使用 malloc 分配的内存需要使用 free 释放
		delete[] this->m_p;
		// 设置指针指为空 , 避免出现野指针
		this->m_p = NULL;
		// 设置字符串长度为 0
		this->m_len = 0;
	}
	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = s.m_len;
	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->m_p = new char[this->m_len + 1];
	// 拷贝字符串到 m_p 指向的内存中
	strcpy(this->m_p, s.m_p);
	cout << "调用重载 等号 = 操作符函数 String& String::operator=(const String& s)" << endl;
	return *this;
}
// 重载等号 = 操作符 , 右操作数是 字符串常量值 的情况
String& String::operator=(const char* p)
{
	// 先处理本对象已分配的内存
	if (this->m_p != NULL)
	{
		// 之前使用 new 分配的内存
		// 释放内存就需要使用 delete 
		// 使用 malloc 分配的内存需要使用 free 释放
		delete[] this->m_p;
		// 设置指针指为空 , 避免出现野指针
		this->m_p = NULL;
		// 设置字符串长度为 0
		this->m_len = 0;
	}
	// 拷贝字符串长度
	// 注意 : 字符串指针 指向的内存空间大小需要 +1 , 内容是 '\0'
	this->m_len = strlen(p);
	// 使用 new 关键字为 char* m_p; 指针分配内存
	// 对于基础数据类型 new 等同于 malloc
	this->m_p = new char[this->m_len + 1];
	// 拷贝字符串到 m_p 指向的内存中
	strcpy(this->m_p, p);
	cout << "调用重载 等号 = 操作符函数 String& String::operator=(const char* p)" << endl;
	return *this;
}
// 重载 数组下标 [] 操作符
char& String::operator[](int i)
{
	cout << "调用重载 下标 [] 操作符函数 char& String::operator[](int i)" << endl;
	// 直接返回对应 i 索引字符
	return this->m_p[i];
}
3、Test.cpp 测试类
#include "iostream"
using namespace std;
// 导入自定义的 String 类
#include "String.h"
int main() {
	// 调用无参构造函数
	String s1;
	// 调用有参构造函数
	String s2("Tom");
	// 调用拷贝构造函数
	String s3 = s2;
	// 调用重载的等号运算符函数, 右操作数是 String 对象
	s1 = s2;
	// 调用重载的等号运算符函数, 右操作数是 字符串常量值 , char* 指针类型
	s3 = "Jerry";
	// 调用重载的下标运算符函数
	char c = s3[3];
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
}
4、执行结果
执行结果 :
调用无参构造函数
调用有参构造函数
调用拷贝构造函数
调用重载 等号 = 操作符函数 String& String::operator=(const String& s)
调用重载 等号 = 操作符函数 String& String::operator=(const char* p)
调用重载 下标 [] 操作符函数 char& String::operator[](int i)




















