C++ 入门基础之六

友元函数

类的友元函数是定义在类的外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型在类的声明中出现过,但是友元函数并不是类的成员函数,而是普通函数(全局函数)。如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend

友元函数的规则

为什么要引入友元函数:

C++ 利用 friend 修饰符,可以让一些设定的函数能够对一些保护数据进行访问,避免把类的成员全部设置成 public,最大限度的保护数据成员的安全。同时友元函数可以实现类之间的数据共享,减少系统开销,提高效率。由于友元函数破环了封装机制,因此推荐尽量使用成员函数,除非不得已的情况下才使用友元函数。

什么时候使用友元函数:

  • 多个类要共享数据的时候
  • 运算符重载的某些场合需要使用友元函数

友元函数的参数:

因为友元函数没有 this 指针,所以参数会有三种情况:

  • a) 要访问非 static 成员时,需要对象做参数
  • b) 要访问 static 成员或全局变量时,则不需要对象做参数
  • c) 如果做参数的对象是全局对象,则不需要对象做参数

友元函数的位置:

  • 因为友元函数是类外的函数(普通函数),所以它的声明可以放在类的私有段(private)或公有段(public),两者都是没有区别的
  • 一个函数可以是多个类的友元函数,只需要在各个类中分别声明即可

友元函数的调用:

  • 可以直接调用友元函数,不需要通过对象或指针
  • 友元函数的调用与普通函数(全局函数)的调用方式和原理一致

友元函数的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

using namespace std;

class A {
public:
A(int a) {
this->a = a;
}

int getA() {
return this->a;
}

// 声明友元函数
friend void update2(A* p);

private:
int a;
};

void update1(A* p) {
// p->a = 30; // 错误写法,在普通函数(全局函数)内,私有数据成员不能在类外被访问
}

void update2(A* p) {
p->a = 30; // 在友元函数内,可以通过对象参数访问私有数据成员
}

int main() {
A* a = new A(10);
update2(a); // 调用友元函数
cout << "a = " << a->getA() << endl;
delete a;
return 0;
}

程序运行的输出结果如下:

1
a = 30

友元类

友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的私有(private)成员和保护(protected)成员。当希望一个类可以访问另一个类的保护数据时,可以将该类声明为另一类的友元类。定义友元类的语法格式为 friend class 类名;,其中类名必须是程序中的一个已定义过的类。值得一提的是,友元类通常设计为一种对数据操作或类之间传递消息的辅助类。

友元类的规则

  • 友元关系不能被继承
  • 友元关系是单向的,不具有交换性。若类 B 是类 A 的友元,则类 A 不一定是类 B 的友元,要看在类 B 中是否有相应的声明
  • 友元关系不具有传递性,若类 B 是类 A 的友元,类 C 是 类 B 的友元,则类 C 不一定是类 A 的友元,要看类 A 中是否有相应的声明

友元类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>

using namespace std;

class A {

public:

// 声明友元类 B
friend class B;

void print() {
cout << "a = " << a << endl;
}

private:
int a;
};

class B {

public:
void setValue(int a) {
aObj.a = a; // 类 B 是类 A 的友元类,因此 B 类的所有成员函数都可以访问 A 类的私有成员或者保护成员
}

void print() {
aObj.print();
}

private:
A aObj;

};

int main() {
B b;
b.setValue(100);
b.print();
return 0;
}

程序运行的输出结果如下:

1
a = 100

运算符重载基础

所谓重载,就是重新赋予新的含义。函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是 一名多用。运算符也可以重载,实际上,开发者已经在不知不觉之中使用了运算符重载。例如,大家都已习惯于用加法运算符 + 对整数、单精度数和双精度数进行加法运算,如 5 + 8,5.8 + 3.67 等,其实计算机对整数、单精度数和双精度数的加法操作过程是很不相同的,但由于 C++ 已经对运算符 + 进行了重载,所以就能适用于 int、float、doUble 类型的运算。又如 << 是 C++ 的位运算中的位移运算符(左移),但在输出操作中又是与流对象 cout 配合使用的流插入运算符。>> 也是位移运算符 (右移),但在输入操作中又是与流对象 cin 配合使用的流提取运算符。这就是运算符重载 (Operator Overloading)。C++ 系统对 <<>> 进行了重载,用户在不同的场合下使用它们时,作用是不同的。对 <<>> 的重载处理是放在头文件 stream 中的。因此,如果要在程序中用 <<>> 作流插入运算符和流提取运算符,必须在本文件模块中包含头文件 stream,当然还应当包括命名空间的使用声明 using namespace std

运算符重载的语法

cplusplus-overload-1

例如:

  • 使用类成员函数完成 "-" 运算符重载的语法:Complex operator-(Complex &c2)
  • 使用友元函数完成 "+" 运算符重载的语法:Complex operator+(Complex &c1, Complex &c2)

运算符重载的限制

cplusplus-overload-6
cplusplus-overload-2

运算符重载的两种方式

cplusplus-overload-3
cplusplus-overload-4
cplusplus-overload-5

前置与后置运算符重载规则

在 C++ 中是通过一个占位参数(int)来区分前置运算符和后置运算符的重载,例如 ++aa++--bb--

cplusplus-overload-7

运算符重载的简单使用案例

二元运算符重载

在下述的案例中,演示了如何使用类成员函数和友元函数实现二元运算符的重载。值得一提的是,除了使用友元函数外,还可以使用全局函数(普通函数)来实现运算符的重载,不同的是使用友元函数更方便,可以直接访问类的所有私有(private)成员和保护(protected)成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>

using namespace std;

class Complex {
private:
int a;
int b;

public:
Complex(int a = 0, int b = 0) {
this->a = a;
this->b = b;
}

void print() {
cout << "a=" << this->a << ", b=" << this->b << endl;
}

public:
// 使用类成员函数完成 "-" 运算符的重载
Complex operator-(Complex& c2) {
Complex c3(this->a - c2.a, this->b - c2.b);
return c3;
}

// 声明用于 "+" 运算符重载的友元函数
friend Complex operator+(Complex& c1, Complex& c2);
};

// 定义友元函数完成 "+" 运算符的重载
Complex operator+(Complex& c1, Complex& c2) {
Complex c3(c1.a + c2.a, c1.b + c2.b);
return c3;
}

int main() {
Complex c1(1, 2), c2(3, 4);

// 直接调用友元函数
Complex c3 = operator+(c1, c2);
c3.print();

// 使用友元函数完成 "+" 运算符的重载
Complex c4 = c1 + c2;
c4.print();

// 直接调用类成员函数
Complex c5 = c1.operator-(c2);
c5.print();

// 使用类成员函数完成 "-" 运算符的重载
Complex c6 = c1 - c2;
c6.print();

return 0;
}

程序运行的输出结果如下:

1
2
3
4
a=4, b=6
a=4, b=6
a=-2, b=-2
a=-2, b=-2

一元运算符重载

在下述的案例中,演示了如何使用类成员函数和友元函数实现一元运算符的重载。值得一提的是,除了使用友元函数外,还可以使用全局函数(普通函数)来实现运算符的重载,不同的是使用友元函数更方便,可以直接访问类的所有私有(private)成员和保护(protected)成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>

using namespace std;

class Complex {
private:
int a;
int b;

public:
Complex(int a = 0, int b = 0) {
this->a = a;
this->b = b;
}

void print() {
cout << "a=" << this->a << ", b=" << this->b << endl;
}

public:
// 使用类成员函数完成 "前置--" 运算符的重载
Complex& operator--() {
this->a--;
this->b--;
return *this;
}

// 使用类成员函数完成 "后置--" 运算符的重载
// 使用占位参数进行函数重载,是为了解决与 "前置--" 类成员函数冲突的问题
Complex operator--(int) {
Complex tmp(this->a, this->b);
this->a--;
this->b--;
return tmp;
}

// 声明用于 "前置++" 运算符重载的友元函数
friend Complex& operator++(Complex& c1);

// 声明用于 "后置++" 运算符重载的友元函数
// 使用占位参数进行函数重载,是为了解决与 "前置++" 友元函数冲突的问题
friend Complex operator++(Complex& c1, int);
};

// 定义友元函数完成 "前置++" 运算符的重载
Complex& operator++(Complex& c1)
{
c1.a++;
c1.b++;
return c1;
}

// 定义友元函数完成 "后置++" 运算符的重载
Complex operator++(Complex& c1, int) {
Complex tmp(c1.a, c1.b);
c1.a++;
c1.b++;
return tmp;
}

int main() {
Complex c1(1, 2), c2(8, 9), c3(15, 16), c4(24, 25);

// 使用友元函数完成 "前置++" 运算符的重载
++c1;
c1.print();

// 使用类成员函数完成 "前置--" 运算符的重载
--c2;
c2.print();

// 使用友元函数完成 "后置++" 运算符的重载
Complex c5 = c3++;
c3.print();
c5.print();

// 使用类成员函数完成 "后置--" 运算符的重载
Complex c6 = c4--;
c4.print();
c6.print();

return 0;
}

程序运行的输出结果如下:

1
2
3
4
5
6
a=2, b=3
a=7, b=8
a=16, b=17
a=15, b=16
a=23, b=24
a=24, b=25

左移运算符的重载

值得一提的是,<< 左移运算符和 >> 右移运算符的重载,只能使用友元函数或者全局函数,不能使用类成员函数,这也是友元函数的重要作用之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

using namespace std;

class Complex {
private:
int a;
int b;

public:
Complex(int a = 0, int b = 0) {
this->a = a;
this->b = b;
}

public:
// 声明友元函数实现 "<<" 左移运算符的重载
friend ostream& operator<<(ostream& out, Complex& c1);
};

// 定义友元函数实现 "<<" 左移运算符的重载
ostream& operator<<(ostream& out, Complex& c1) {
out << "a=" << c1.a << ", b=" << c1.b << endl;
return out;
}

int main() {
Complex c1(1, 2), c2(6, 9);
cout << c1 << c2;
return 0;
}

程序运行的输出结果如下:

1
2
a=1, b=2
a=6, b=9

等号运算符的重载

  • = 运算符的结合性是从右到左
  • = 运算符的重载用于对象数据的复制
  • 必须通过类成员函数重载 = 运算符,不能使用友元函数
  • = 运算符重载的函数原型为:类型 & 类名 :: operator= ( const 类名 & ) ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include "string.h"

using namespace std;

class Name {

private:
char* p;
int len;

public:

Name(const char* name) {
cout << "有参构造函数被调用了" << endl;
len = strlen(name);
p = new char[len + 1];
strcpy(p, name);
}

// 深拷贝的实现
Name(const Name& name) {
cout << "拷贝构造函数被调用了" << endl;
len = name.getLen();
p = new char[len + 1];
strcpy(p, name.getP());
}

~Name() {
cout << "析构函数被调用了" << endl;
if (p != NULL) {
delete[] p;
p = NULL;
len = 0;
}
}

char* getP() const {
return p;
}

int getLen() const {
return len;
}

public:
// 使用类成员函数实现 "=" 运算符的重载
Name& operator=(const Name& n) {
// 释放内存空间
if (p != NULL) {
delete[] p;
p = NULL;
len = 0;
}
// 重新分配内存空间
len = n.getLen();
p = new char[len + 1];
strcpy(p, n.getP());
return *this;
}
};

int main() {
Name obj1("Peter");
Name obj2("Tom");
Name obj4("Tim");

// 会自动调用拷贝构造函数(属于深拷贝)
Name obj3 = obj1;
cout << "obj3.name: " << obj3.getP() << ", obj3.len: " << obj3.getLen() << endl;

// 不会自动调用拷贝构造函数(属于浅拷贝)
// 默认情况下,若这里不对 "=" 运算符进行重载,最终程序会异常终止运行(由于同一块内存空间被释放两次导致)
obj4 = obj1;
cout << "obj4.name: " << obj4.getP() << ", obj4.len: " << obj4.getLen() << endl;

return 0;
}

程序运行的输出结果如下:

1
2
3
4
5
6
7
8
9
10
有参构造函数被调用了
有参构造函数被调用了
有参构造函数被调用了
拷贝构造函数被调用了
obj3.name: Peter, obj3.len: 5
obj4.name: Peter, obj4.len: 5
析构函数被调用了
析构函数被调用了
析构函数被调用了
析构函数被调用了

函数运算符的重载

在下述的案例中,演示了如何使用类成员函数重载函数运算符 (),值得一提的是,不能用友元函数重载函数运算符 ()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

using namespace std;

class Test {
public:
int operator()(int a, int b) {
return a + b;
}
};

int main() {
Test test;
cout << test(3, 4) << endl;
return 0;
}

程序运行的输出结果如下:

1
7

运算符重载进阶

为什么不要重载 && 和 || 操作符

  • a) &&|| 是 C++ 中非常特殊的操作符
  • b) &&|| 内置实现了短路规则
  • c) 操作符重载是靠函数重载来完成的
  • d) 操作数作为函数参数传递
  • e) C++ 的函数参数都会被求值,无法实现短路规则

不同函数实现运算符重载的应用场景

友元函数和类成员函数的选择方法:

  • a) =[]()-> 运算符,只能通过类成员函数进行重载
  • b) 当无法修改左操作数的类时,只能通过友元函数进行重载,例如 <<>> 运算符

友元函数重载 <<>> 运算符:

  • istreamostream 是 C++ 的预定义流类
  • cinistream 的对象,coutostream 的对象
  • 运算符 <<ostream 重载为插入操作,用于输出基本类型数据
  • 运算符 >>istream 重载为提取操作,用于输入基本类型数据
  • 只能使用友元函数或者全局函数重载 <<>> 运算符,输出和输入用户自定义的数据类型

类成员函数与友元函数实现运算符重载的步骤:

  • a) 要承认运算符重载是一个函数,写出函数名称,如 operator +()
  • b) 根据操作数,写出函数参数
  • c) 根据业务,完善函数的返回值(看函数是返回引用、指针还是元素),及实现函数业务;例如当函数的返回值充当左值时,需要返回一个引用

使用友元函数重载运算符的注意事项

  • a) 友元函数重载运算符常用于运算符的左右操作数类型不相同的场景
  • b) 在函数的第一个参数需要隐式转换的情形下,使用友元函数重载运算符是正确的选择
  • c) 友元函数没有 this 指针,所需操作数都必须在函数的参数表中显式声明,很容易实现类型的隐式转换
  • d) 在 C++ 中不能用友元函数重载的运算符分别有:=[]()->
  • e) 在 C++ 中不要重载 &&|| 运算符
  • f) C++ 的运算符重载遵循函数重载的规则

运算符重载的综合使用案例

重载自定义数组类的各种运算符

在本案例中,自定义了数组类 Array,并使用类成员函数分别对 Array 类的 []===!= 运算符进行重载。

★点击显示完整的案例代码★
  • Array.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#pragma once

#include <iostream>

using namespace std;

class Array {
public:
Array(int length);

Array(const Array& array);

~Array();

public:
int length();

public:
// 使用类成员函数重载 "[]" 数组下标运算符,用于数组元素的赋值和取值
int& operator[](int index);

// 使用类成员函数重载 "=" 运算符,用于数组之间的赋值
Array& operator=(const Array& array);

// 使用类成员函数重载 "==" 运算符,判断两个数组是否相同
bool operator==(const Array & array);

// 使用类成员函数重载 "!=" 运算符,判断两个数组是否不相同
bool operator!=(const Array& array);

private:
int m_length;
int* m_space;
};
  • Array.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include "Array.h"

Array::Array(int length) {
cout << "有参构造函数被调用" << endl;
if (length < 0) {
length = 0;
}
this->m_length = length;
this->m_space = new int[length];
}

Array::Array(const Array& array) {
cout << "拷贝构造函数被调用" << endl;
// 深拷贝,单独分配内存空间
this->m_length = array.m_length;
this->m_space = new int[array.m_length];
for (int i = 0; i < array.m_length; i++) {
this->m_space[i] = array.m_space[i];
}
}

Array::~Array() {
cout << "析构函数被调用" << endl;
if (this->m_space != NULL) {
delete[] this->m_space;
this->m_space = NULL;
this->m_length = 0;
}
}

// 使用类成员函数重载 "[]" 数组下标运算符,用于数组元素的赋值和取值
int& Array::operator[](int index) {
return this->m_space[index];
}

// 使用类成员函数重载 "=" 运算符,用于数组之间的赋值
Array& Array::operator=(const Array& array) {
if (this->m_space != NULL) {
delete[] this->m_space;
this->m_space = NULL;
this->m_length = 0;
}
// 深拷贝,单独分配内存空间
this->m_length = array.m_length;
this->m_space = new int[array.m_length];
for (int i = 0; i < array.m_length; i++) {
this->m_space[i] = array.m_space[i];
}
return *this;
}

// 使用类成员函数重载 "==" 运算符,判断两个数组是否相同
bool Array::operator==(const Array& array) {
if (this->m_length != array.m_length) {
return false;
}
for (int i = 0; i < this->m_length; i++) {
if (this->m_space[i] != array.m_space[i]) {
return false;
}
}
return true;
}

// 使用类成员函数重载 "!=" 运算符,判断两个数组是否不相同
bool Array::operator!=(const Array& array) {
return !(*this == array);
}

int Array::length() {
return this->m_length;
}
  • main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include "Array.h"

using namespace std;

int main() {

// 自动调用构造函数
Array array1(5);

for (int i = 0; i < array1.length(); i++) {
array1[i] = i;
}

for (int i = 0; i < array1.length(); i++) {
cout << "array1[" << i << "] = " << array1[i] << endl;
}

// 自动调用拷贝构造函数(属于深拷贝)
Array array2 = array1;

for (int i = 0; i < array2.length(); i++) {
cout << "array2[" << i << "] = " << array2[i] << endl;
}

// 自动调用拷贝构造函数(属于深拷贝)
Array array3 = array1;

// 不会自动调用拷贝构造函数(属于浅拷贝)
// 默认情况下,若这里不对 "=" 运算符进行重载,最终程序会异常终止运行(由于同一块内存空间被释放两次导致)
array3 = array2;
for (int i = 0; i < array3.length(); i++) {
cout << "array3[" << i << "] = " << array3[i] << endl;
}

// 判断两个数组是否相同
bool result1 = array1 == array2;
string strResult1 = result1 ? "=" : "!=";
cout << "array1 " << strResult1 << " array2 " << endl;

// 判断两个数组是否不相同
bool result2 = array1 != array2;
string strResult2 = result2 ? "!=" : "=";
cout << "array1 " << strResult2 << " array2 " << endl;

return 0;
}

程序运行的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
有参构造函数被调用
array1[0] = 0
array1[1] = 1
array1[2] = 2
array1[3] = 3
array1[4] = 4
拷贝构造函数被调用
array2[0] = 0
array2[1] = 1
array2[2] = 2
array2[3] = 3
array2[4] = 4
拷贝构造函数被调用
array3[0] = 0
array3[1] = 1
array3[2] = 2
array3[3] = 3
array3[4] = 4
array1 = array2
array1 = array2
析构函数被调用
析构函数被调用
析构函数被调用

重载自定义字符串类的各种运算符

在本案例中,自定义了字符串类 MyString,并使用类成员函数和友元函数分别对 MyString 类的 []===!=><>><< 运算符进行重载。

★点击显示完整的案例代码★
  • MyString.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#pragma once

#include <iostream>
#include "string.h"

using namespace std;

class MyString {

public:
MyString();
MyString(int len);
MyString(const char* p);
MyString(const MyString& str);

public:
~MyString();

public:
// 使用类成员函数重载 "[]" 运算符
char& operator[](int index);

// 使用类成员函数重载 "=" 运算符
MyString& operator=(const char* p);
MyString& operator=(const MyString& str);

// 使用类成员函数重载 "==" 运算符
bool operator==(const char* p) const;
bool operator==(const MyString str) const;

// 使用类成员函数重载 "!=" 运算符
bool operator!=(const char* p) const;
bool operator!=(const MyString str) const;

// 使用类成员函数重载 ">" 运算符
bool operator>(const char* p) const;
bool operator>(const MyString str) const;

// 使用类成员函数重载 "<" 运算符
bool operator<(const char* p) const;
bool operator<(const MyString str) const;

// 使用友元函数重载 "<<" 运算符
friend ostream& operator<<(ostream& out, MyString& str);

// 使用友元函数重载 ">>" 运算符
friend iostream& operator>>(iostream& in, MyString& str);

public:
int length();
char* c_str();

private:
int m_length;
char* m_space;
};
  • MyString.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "MyString.h"

// 无参构造函数
MyString::MyString() {
// 初始化为空字符串
this->m_length = 0;
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, "");
}

// 有参构造函数
MyString::MyString(int len) {
if (len < 0) {
len = 0;
}
// 初始化为空字符串
this->m_length = len;
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, "");
}

// 有参构造函数
MyString::MyString(const char* p) {
if (p == NULL) {
// 初始化为空字符串
this->m_length = 0;
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, "");
}
else {
this->m_length = strlen(p);
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, p);
}
}

// 拷贝构造函数
MyString::MyString(const MyString& str) {
// 深拷贝,重新分配内存空间
this->m_length = str.m_length;
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, str.m_space);
}

// 析构函数
MyString::~MyString() {
// 释放内存空间
if (this->m_space != NULL) {
delete[] this->m_space;
this->m_space = NULL;
this->m_length = 0;
}
}

// 使用类成员函数重载 "[]" 运算符
char& MyString::operator[](int index) {
return this->m_space[index];
}

// 使用类成员函数重载 "=" 运算符
MyString& MyString::operator=(const char* p) {
// 释放内存空间
if (this->m_space != NULL) {
delete[] this->m_space;
this->m_space = NULL;
this->m_length = 0;
}
// 深拷贝,重新分配内存空间
if (p == NULL) {
// 初始化为空字符串
this->m_length = 0;
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, "");
}
else {
this->m_length = strlen(p);
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, p);
}
return *this;
}

// 使用类成员函数重载 "=" 运算符
MyString& MyString::operator=(const MyString& str) {
// 释放内存空间
if (this->m_space != NULL) {
delete[] this->m_space;
this->m_space = NULL;
this->m_length = 0;
}
// 深拷贝,重新分配内存空间
this->m_length = str.m_length;
this->m_space = new char[this->m_length + 1];
strcpy(this->m_space, str.m_space);
return *this;
}

// 使用类成员函数重载 "==" 运算符
bool MyString::operator==(const char* p) const {
if (p == NULL) {
if (this->m_length == 0) {
return true;
}
return false;
}
if (this->m_length != strlen(p)) {
return false;
}
return !strcmp(this->m_space, p);
}

bool MyString::operator==(const MyString str) const {
if (this->m_length != str.m_length) {
return false;
}
return !strcmp(this->m_space, str.m_space);
}

// 使用类成员函数重载 "!=" 运算符
bool MyString::operator!=(const char* p) const {
return !(*this == p);
}

bool MyString::operator!=(const MyString str) const {
return !(*this == str);
}

// 使用类成员函数重载 ">" 运算符
bool MyString::operator>(const char* p) const {
return strcmp(p, this->m_space) < 0;
}

bool MyString::operator>(const MyString str) const {
return strcmp(str.m_space, this->m_space) < 0;
}

// 使用类成员函数重载 "<" 运算符
bool MyString::operator<(const char* p) const {
return strcmp(this->m_space, p) < 0;
}

bool MyString::operator<(const MyString str) const {
return strcmp(this->m_space, str.m_space) < 0;
}

// 使用友元函数重载 "<<" 运算符
ostream& operator<<(ostream& out, MyString& str) {
out << str.m_space;
return out;
}

// 使用友元函数重载 ">>" 运算符
iostream& operator>>(iostream& in, MyString& str)
{
in >> str.m_space;
return in;
}

int MyString::length()
{
return this->m_length;
}

char* MyString::c_str() {
return this->m_space;
}
  • main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "MyString.h"

int main() {

// 自动调用有参构造函数
MyString str1("Tom");
MyString str2(NULL);
MyString str3("Peter");

// 自动调用拷贝构造函数
MyString str4 = str1;

// 重载 "<<" 运算符
cout << "str2 = " << str2 << endl;
cout << "str4 = " << str4 << endl;
cout << endl;

// 不会自动调用拷贝构造函数(属于浅拷贝)
// 重载 "=" 运算符,实现深拷贝
str4 = str3;
cout << "str4 = " << str4 << endl;
str4 = "Jim";
cout << "str4 = " << str4 << endl;
str4 = NULL;
cout << "str4 = " << str4 << endl;
cout << endl;

// 重载 "[]" 运算符
MyString str5("David");
str5[0] = 'F';
cout << "str5[0] = " << str5[0] << endl;
cout << "str5 = " << str5 << endl;
cout << endl;

// 重载 "==" 运算符
MyString str6("Aaron");
MyString str7 = str6;
cout << str6 << (str6 == str7 ? " = " : " != ") << str7 << endl;

// 重载 "!=" 运算符
cout << str6 << (str6 != NULL ? " != " : " = ") << " NULL" << endl;
cout << endl;

// 重载 "<" 运算符
MyString str8("AAAA");
MyString str9("BBBB");
cout << str8 << (str8 < str9 ? " < " : " > ") << str9 << endl;
cout << str8 << (str8 < "CCCC" ? " < " : " > ") << "CCCC" << endl;

// 重载 ">" 运算符
cout << str9 << (str9 > str8 ? " > " : " < ") << str8 << endl;
cout << str9 << (str9 > "DDDD" ? " > " : " < ") << "DDDD" << endl;
cout << endl;

// 重载 ">>" 运算符
MyString str11(5);
cout << "请输入长度为 5 的字符串:" << endl;
cin >> str11.c_str();
cout << "str11 = " << str11 << endl;

// MyString str4 = NULL; 此写法,会自动调用有参构造函数 `MyString(const char* p);`

// MyString str1("AB");
// MyString str2 = str1;
// str2 = NULL: 此写法,会自动调用 "=" 运算符重载的函数 `bool operator==(const char* p) const;`

return 0;
}

程序运行的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
str2 =
str4 = Tom

str4 = Peter
str4 = Jim
str4 =

str5[0] = F
str5 = Favid

Aaron = Aaron
Aaron != NULL

AAAA < BBBB
AAAA < CCCC
BBBB > AAAA
BBBB < DDDD

请输入长度为 5 的字符串:
abcde
str11 = abcde

C++ 运算符和结合性的附录

cplusplus-overload-9
cplusplus-overload-10