C++实现string类

C++实现string类

string类

写一个string类,能够完成string类的操作:
1. 构造函数,能使用=构造,构造函数传入的参数为const char* 类型
2. 拷贝构造函数
3. 析构函数
4. operator =
5. operator +
6. operator +=
7. operator <<
8. 从string类型转换成const char*类型,data()方法
9. 将string转成int类型
10. 统计string的长度,也就是size()方法
11. 清空字符串
12. 判断字符串是否为空
13. 追加append函数,类似于+=

数据成员

  • char* ptr:指向堆内存空间,从堆内存申请空间。
  • int len:记录字符串实际长度,不包含结束符。

默认构造函数

默认构造函数中,为ptr申请一个字节的堆内存,这样任何情况下,ptr都不为nullptr,避免了ptr是否为nullptr的情况,在随时访问的时候,就可以少写很多if判断。

1
2
3
4
5
myString::myString(){
ptr = (char*)malloc(1); //为ptr申请一个字节的堆内存
memset(ptr,0,1);
len = 0; //字符串实际长度为0
}

含参构造函数

  1. 传入一个const char* r,让构造函数接受字符串字面量作为参数,因为字符串字面量在C++中是const char[]类型的,自然会转换为const char*类型。
  2. 先为ptr申请字符串r长度+1的内存空间,多一个字节是为了保存结束符’\0’。
  3. 再将r所指的内存空间中的内容,拷贝给ptr所指的内存空间,使用memmove,memmove被用来将字符串从一个内存位置复制到另一个位置。
  4. 在实际的调用中,用一个“=”赋值的时候,就会调用含餐构造函数。
1
2
3
4
5
6
7
8
myString::myString(const char* r){
// 先为ptr申请字符串r长度+1的内存空间,多一个字节是为了保存结束符'\0'
len = strlen(r);
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);
// 将r所指的内存空间中的内容,拷贝给ptr所指的内存空间,为了避免内存泄漏,使用memmove
memmove(ptr,r,len+1);
}

析构函数

  1. 释放ptr指向的内存空间
1
2
3
myString::~myString(){
free(ptr);
}

拷贝构造函数

  1. 因为有指针进行new堆内存操作,需要重写拷贝构造函数进行深拷贝。
  2. 如果不重写拷贝构造函数的话,会调用系统默认的拷贝构造。
  3. 默认的拷贝构造是浅拷贝,在包含堆内存操作的类中,会出现重复释放,造成段错误。
1
2
3
4
5
6
7
8
9
// 拷贝构造,深拷贝
myString::myString(const myString& r){
//free(ptr);
len = r.len;
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);// 为p的ptr申请独立的空间
// 将r中的ptr拷贝给p的ptr
memmove(ptr,r.ptr,len+1); // 拷贝数据
}

赋值运算符

  • 防止自赋值
  • 注意对原本非空指针的释放
1
2
3
4
5
6
7
8
9
10
11
myString myString::operator= (const myString& r){
if(this != &r) { // 防止自赋值
if(ptr != nullptr)
free(ptr);
len = r.len;
ptr = (char*)malloc(len+1);
memset(ptr, 0, len+1);
memmove(ptr, r.ptr, len+1);
}
return *this; // 应该返回对象的引用
}

加号运算符和+=运算符

  • p1 = p1+”hello”
  • p1 = p1+p2; 两种都有可能,要写全参数类型
  • +=运算符和+类似
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// p1 = p1+"hello";
myString myString::operator+(const char* r){
// 创建新的对象,新对象的长度以及ptr指向内存空间是this+r的总和
myString newStr;
free(newStr.ptr); //这里要把newStr.ptr指向的内存进行释放,否则会出现内存泄漏
newStr.len = this->len+strlen(r);
newStr.ptr = (char*)malloc(newStr.len+1);
memset(newStr.ptr,0,newStr.len+1);
memmove(newStr.ptr,ptr,len);
strcat(newStr.ptr,r);
return newStr;
}

// p1 = p1+p2
myString myString::operator+(const myString& r){
myString newStr;
free(newStr.ptr); //这里要把newStr.ptr指向的内存进行释放,否则会出现内存泄漏
newStr.len = this->len+r.len;
newStr.ptr = (char*)malloc(newStr.len+1);
memset(newStr.ptr,0,newStr.len+1);
memmove(newStr.ptr,ptr,len);
strcat(newStr.ptr,r.ptr);
return newStr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
myString myString::operator+=(const char* r){
char* temp = ptr;
int l = len; // 保存原来的ptr和len
this->len = this->len+strlen(r);
this->ptr = (char*)malloc(this->len+1); //ptr指向新的内存空间
memset(ptr,0,len+1);
memmove(ptr,temp,l); // 把原来的ptr内容拷贝给新的ptr
strcat(ptr,r);
free(temp);
return *this;
}


myString myString::operator+=(const myString& r){
char* temp = ptr;
int l = len;
len = len+r.len;
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);
memmove(ptr,temp,l);
strcat(ptr,r.ptr);
free(temp); //temp记得free
return *this;
}

代码

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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include<iostream>
#include <string.h>

using std::cout;
using std::endl;

/**
* 当myString对象没有保存任何字符串的时候,应该至少为该对象中的ptr申请至少一个字节的空间
* 这样任何情况下,ptr都不为nullptr,避免了ptr是否为nullptr的情况,在随时访问的时候,
* 就可以少写很多if判断。
* · 无参构造函数要重写。
* · 因为有指针进行new堆内存操作,默认的拷贝构造是浅拷贝,需要重写拷贝构造,否则会出现重复释放,造成段错误。
*/
class myString{
public:
myString(); //无参构造函数
myString(const char* r); //单参构造函数
myString(const myString& r); //单参构造函数
~myString();
void show(){cout<<ptr<<endl;}
int size(){return len;}
void clear();
bool empty();
myString operator= (const myString& r);
myString operator+ (const char* r);
myString operator+(const myString& r);
myString operator+= (const char* r);
myString operator+= (const myString& r);
char operator[](int n);

void append(const char* r);
void append(const myString& r);

friend std::ostream& operator<< (std::ostream& out, const myString& r);
friend std::istream& operator>> (std::ostream& in, myString& r);

const char* data(){return this->ptr;};
int toInt();
private:
char* ptr; //指向堆内存空间
int len; // 字符串实际长度,不包含结束符
};

myString::myString(){
ptr = (char*)malloc(1); //为ptr申请一个字节的堆内存
memset(ptr,0,1);
len = 0;
}

myString::myString(const char* r){
// 先为ptr申请字符串r长度+1的内存空间,多一个字节是为了保存结束符'\0'
len = strlen(r);
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);
// 将r所指的内存空间中的内容,拷贝给ptr所指的内存空间,为了避免内存泄漏,使用memmove
memmove(ptr,r,len+1);
}

// 拷贝构造,深拷贝
myString::myString(const myString& r){
len = r.len;
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);// 为p的ptr申请独立的空间
// 将r中的ptr拷贝给p的ptr
memmove(ptr,r.ptr,len+1); // 拷贝数据
}

myString::~myString(){
free(ptr);
}

void myString::clear(){
free(ptr);
ptr=(char*) malloc(1);
memset(ptr,0,1);
len = 0;
}

bool myString::empty(){
return !len;
}

myString myString::operator= (const myString& r){
if(this != &r) { // 防止自赋值
if(ptr != nullptr)
free(ptr);
len = r.len;
ptr = (char*)malloc(len+1);
memset(ptr, 0, len+1);
memmove(ptr, r.ptr, len+1);
}
return *this; // 应该返回对象的引用。
}

// p1 = p1+"hello";
myString myString::operator+(const char* r){
// 创建新的对象,新对象的长度以及ptr指向内存空间是this+r的总和
myString newStr;
free(newStr.ptr); //这里要把newStr.ptr指向的内存进行释放,否则会出现内存泄漏
newStr.len = this->len+strlen(r);
newStr.ptr = (char*)malloc(newStr.len+1);
memset(newStr.ptr,0,newStr.len+1);
memmove(newStr.ptr,ptr,len);
strcat(newStr.ptr,r);
return newStr;
}

// p1 = p1+p2
myString myString::operator+(const myString& r){
myString newStr;
free(newStr.ptr); //这里要把newStr.ptr指向的内存进行释放,否则会出现内存泄漏
newStr.len = this->len+r.len;
newStr.ptr = (char*)malloc(newStr.len+1);
memset(newStr.ptr,0,newStr.len+1);
memmove(newStr.ptr,ptr,len);
strcat(newStr.ptr,r.ptr);
return newStr;
}

myString myString::operator+=(const char* r){
char* temp = ptr;
int l = len; // 保存原来的ptr和len
this->len = this->len+strlen(r);
this->ptr = (char*)malloc(this->len+1); //ptr指向新的内存空间
memset(ptr,0,len+1);
memmove(ptr,temp,l); // 把原来的ptr内容拷贝给新的ptr
strcat(ptr,r);
free(temp);
return *this;
}


myString myString::operator+=(const myString& r){
char* temp = ptr;
int l = len;
len = len+r.len;
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);
memmove(ptr,temp,l);
strcat(ptr,r.ptr);
free(temp); //temp记得free
return *this;
}

char myString::operator[](int n){
return ptr[n];
}

void myString::append(const char* r){
char* temp = ptr;
int l = len; // 保存原来的ptr和len
this->len = this->len+strlen(r);
this->ptr = (char*)malloc(this->len+1); //ptr指向新的内存空间
memset(ptr,0,len+1);
memmove(ptr,temp,l); // 把原来的ptr内容拷贝给新的ptr
strcat(ptr,r);
free(temp);
}

void myString::append(const myString& r){
char* temp = this->ptr;
int l = len;
len = len+r.len;
ptr = (char*)malloc(len+1);
memset(ptr,0,len+1);
memmove(ptr,temp,l);
strcat(ptr,r.ptr);
free(temp); //temp记得free
}

std::ostream& operator<< (std::ostream& out, const myString& r){
out<<r.ptr;
return out;
}

std::istream& operator>> (std::ostream& in, myString& r){
char buf[1024];
std::cin>>buf;
free(r.ptr);
r.len = strlen(buf);
r.ptr = (char*)malloc(r.len+1);
memset(r.ptr,0,r.len+1);
memmove(r.ptr,buf,r.len+1);

}

int myString::toInt(){
return atoi(this->ptr);
}

int main(){
myString str = "hello"; // 转换构造函数,也就是单参构造函数
myString ptr(str); // 拷贝构造
myString atp = str; //拷贝构造
ptr = str; //运算符 operator=
ptr = ptr + " world";
ptr.show();
myString str2 = " world";
str2 = str+str2;
str += " world";
str.show();
ptr+=str2;
ptr.show();
atp.append(" world");
atp.show();
return 0;
}

C++实现string类
https://cauccliu.github.io/2024/03/26/C++实现string类/
Author
Liuchang
Posted on
March 26, 2024
Licensed under