自实现 myString 类主要目的是剖析系统内部的 string 类的一些实现方法以及加强对类封装、运算符重载等特性的掌握。其中包含了几项非常重要的功能实现。

  1. 使用构造器创建对象。
  2. 拷贝构造器创建对象。
  3. 赋值运算符重载构造对象。
  4. []运算符重载构造对象数组。
  5. ==运算符重载判断对象是否相等。
  6. +运算符重载实现对象相加。
  7. >> << 流输入输出运算符实现打印和输入。

具体的实现代码分三个部分,一个 MyString.h 文件,包含类的声明和结构。一个 MyString.cpp 文件,包含类的成员及友元函数实现。最终是一个 main.cpp 来测试我们自己的 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
#pragma once
#include <iostream>

class CMyString
{
public:
// 构造器
CMyString(char* str = NULL);
// 拷贝构造器原型,another为传递进来的同类对象
CMyString(const CMyString & another);
// 赋值运算符重载,系统会提供默认,但同样是等位拷贝
CMyString& operator=(const CMyString & another);
// []运算符重载
char operator[](int idx);
// ==运算符重载
bool operator==(const CMyString& another);
// + 运算符重载
CMyString operator+(const CMyString& another);
// 流输入输出
friend std::ostream& operator<<(std::ostream& os, CMyString& another);
friend std::istream& operator<<(std::istream& is, CMyString& another);

~CMyString(void);
char* c_str();
private:
char* m_str;
};

【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
#include "MyString.h"
#include <cstring>


CMyString::CMyString(char* str)
{
if (str)
{
int len = strlen(str);
m_str = new char[len + 1];
strcpy_s(m_str, len + 1, str);
}
else
{
m_str = new char[1];
*m_str = '\0';
}
}

CMyString::CMyString(const CMyString & another)
{
// 浅拷贝,等位拷贝,会造成 double free
// 这样操作会导致 m_str 和 another.m_str 指向同一块堆内存
// 析构时会造成 delete 了同一块内存
// m_str = another.m_str;

// 深拷贝,根据another.m_str的长度给对象new一块内存使双方不冲突
int len = strlen(another.m_str);
m_str = new char[len + 1];
strcpy_s(m_str, len + 1, another.m_str);
}

CMyString& CMyString::operator=(const CMyString & another)
{
if (this == &another)
{
// 解决 s6 = s6 会导致崩溃的问题
return *this;
}
delete [] m_str;
int len = strlen(another.m_str);
m_str = new char[len + 1];
strcpy_s(m_str, len + 1, another.m_str);
return *this;
}

char CMyString::operator[](int idx)
{
return m_str[idx];
}

bool CMyString::operator==(const CMyString& another)
{
if (strcmp(this->m_str, another.m_str) == 0)
return true;
else
return false;
}

CMyString CMyString::operator+(const CMyString& another)
{
int len = 0;
len = strlen(this->m_str);
len += strlen(another.m_str);

CMyString tmp;
delete [] tmp.m_str;

tmp.m_str = new char[len + 1];
memset(tmp.m_str, 0, len + 1);
strcat_s(tmp.m_str, len + 1, this->m_str);
strcat_s(tmp.m_str, len + 1, another.m_str);

return tmp;
}

std::ostream& operator<<(std::ostream& os, CMyString& another)
{
os << another.m_str;
return os;
}

std::istream& operator<<(std::istream& is, CMyString& another)
{
// 先删除了原来保存的数据
delete [] another.m_str;
char buffer[BUFSIZ];
is.getline(buffer, BUFSIZ);
int length = strlen(buffer);
another.m_str = new char[length + 1];
strcpy_s(another.m_str, length + 1, buffer);
return is;
}

CMyString::~CMyString(void)
{
std::cout << "delete [] m_str : " << m_str << std::endl;
delete [] m_str;
}

char* CMyString::c_str()
{
return m_str;
}

【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
#include <iostream>
#include "MyString.h"

using namespace std;

int main(int argc, char* argv[])
{
// 构造函数,无参数
CMyString s;
cout << s.c_str() << endl;

// 构造函数,有参数
CMyString s1("china");
cout << s1.c_str() << endl;

// 类型转化函数
CMyString s2 = "china";
cout << s2.c_str() << endl;

/*
以上均是调用构造器来创建的
*/

// 拷贝构造器
CMyString s3(s2);
cout << s3.c_str() << endl;

// 同上
CMyString s4 = s3;
cout << s4.c_str() << endl;

// 赋值,=号运算符重载
CMyString s5;
// 等同于 s5.operator= (s4);
s5 = s4;
// 同上 相当于s5.operator=(s4.operator=(s3));
s5 = s4 = s3;
cout << s5.c_str() << endl;

CMyString s6("china");
// 这样操作会进入=号运算符重载的函数内
// 但刚进入后 s6 就被 delete 了
// 如果继续后面的操作将会导致访问违规内存而报错
// 个别系统环境下可能不报错,带不代表就是正确的
s6 = s6;
cout << s6.c_str() << endl;

// []运算符重载
cout << s6[2] << endl;

// ==运算符重载
if (s5 == s6) cout << "s5 == s6" << endl;

// + 运算符重载
CMyString s7 = s6 + s5;
cout << s7.c_str() << endl;

cout << s7 << endl;

getchar();
return 0;
}