目录
前言:
C++中的string类是专门用于处理字符串的,比如对字符串的增删查改、以及对字串进行各种操作,当然,上面说到的这些在c语言中一样可以使用字符数组实现,那为什么还要费尽心思的去专门实现一个类来解决字符串相关的问题呢,原因就是C++中的string类对边界访问更严格,并且用string类操作字符串相对于c语言中操作字符数组更加简便。
一、string的模拟实现
string类是STL(标准模板库)中的八大容器之一,STL又是C++中标准库的一部分,简单来说就是库里面已经写好了一个string类,程序员只需要调用该string类就能操作字符串(使用库里的string类要包含头文件<string>),调用该类很简单,但是只有通过了解string类的底层实现是如何实现,并且自己模拟实现出一个string,才能更进一步的了解string类。
1、初始化字符串
首先写一个类,可以实现字符串的创建和打印,初始化字符串代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace ZH//把string放在命名空间中,放在与库里的string重名
{
class string
{
public:
string(const char* str = "")//构造函数初始化对象
:_size(strlen(str))
{
_capacity = _size;
//实际上会给\0开一个空间,但是capacity不记录该空间
_str = new char[_capacity + 1];
strcpy(_str, str);
}
size_t size()const//私有域不可直接被外部访问,因此需要用函数返回_size
{
return _size;
}
char& operator[](size_t i)//为了让外部能用[]访问字符串的内容
{
assert(i < _size);
return _str[i];
}
const char* c_str()//返回首元素地址(类似数组名的作用)
{
return _str;
}
~string()//析构函数,释放_str申请的空间
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;//在堆上开辟一块空间用于存放字符串
size_t _size;//记录字符串的总字符数
size_t _capacity;//记录所开辟空间的大小
};
}
int main()
{
ZH::string st1("hello world");
ZH::string st2;
for (size_t i = 0; i < st1.size(); i++)
{
cout << st1[i] << " ";
}
cout << endl;
for (size_t i = 0; i < st2.size(); i++)
{
cout << st2[i] << " ";
}
cout << endl;
cout << st1.c_str() << endl;//用c语言的打印字符串方式,传首地址打印
cout << st2.c_str() << endl;
return 0;
}
运行结果:

string类的成员变量具体含义作用如下图所示:

2、拷贝构造
我们知道拷贝构造如果不自己实现,那么系统会自动生成一个拷贝构造并且调用,但是系统自动生成的拷贝构造只能完成浅拷贝(即值拷贝)的场景,比如string类的拷贝就不能用浅拷贝完成,具体原因如下:

因此系统自己生成的浅拷贝不能完成这类场景的拷贝,需要我们手动写一个拷贝构造函数完成深拷贝。
演示拷贝构造代码如下(将拷贝构造代码放到上文的代码中也同样可以实现,这里省去了与拷贝构造代码无关的代码):
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace ZH//把string放在命名空间中,放在与库里的string重名
{
class string
{
public:
string(const char* str = "")//构造函数初始化对象
:_size(strlen(str))
{
_capacity = _size;
//实际上会给\0开一个空间,但是capacity不记录该空间
_str = new char[_capacity + 1];
strcpy(_str, str);
}
const char* c_str()//返回首元素地址(类似数组名的作用)
{
return _str;
}
string(const string& s)//深拷贝-拷贝构造
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[_capacity + 1];//开辟一块独立的空间
strcpy(_str, s._str);
}
~string()//析构函数,释放_str申请的空间
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;//在堆上开辟一块空间用于存放字符串
size_t _size;//记录字符串的总字符数
size_t _capacity;//记录所开辟空间的大小
};
}
int main()
{
ZH::string st1("hello world");
ZH::string st2(st1);//将st1的内容拷贝给st2
cout << st1.c_str() << endl;//用c语言的打印字符串方式,传首地址打印
cout << st2.c_str() << endl;
return 0;
}
运行结果:

3、赋值重载
赋值的时候要考虑以下几个问题:

由于以上几个问题涉及的点太多,而且过程过于复杂或造成不必要的消耗,因此库里面的string类在面对赋值时是重新开辟一块空间然后把s1的数据拷贝到该空间中,并且让s2指向该空间即可。
模拟实现的赋值重载代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace ZH//把string放在命名空间中,放在与库里的string重名
{
class string
{
public:
string(const char* str = "")//构造函数初始化对象
:_size(strlen(str))
{
_capacity = _size;
//实际上会给\0开一个空间,但是capacity不记录该空间
_str = new char[_capacity + 1];
strcpy(_str, str);
}
const char* c_str()//返回首元素地址(类似数组名的作用)
{
return _str;
}
string& operator=(const string& s)//赋值重载
{
if (&s != this)
{
char* temp = new char[s._capacity+1];//重新开辟一块空间
strcpy(temp, s._str);//把数据拷贝到该空间中
delete[] _str;//释放拷贝对象的原先空间内容
_str = temp;//让拷贝对象指向该空间
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
~string()//析构函数,释放_str申请的空间
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;//在堆上开辟一块空间用于存放字符串
size_t _size;//记录字符串的总字符数
size_t _capacity;//记录所开辟空间的大小
};
}
int main()
{
ZH::string st1("hello world");
ZH::string st2("zdzdzzdzd");
st2 = st1;
cout << st1.c_str() << endl;//用c语言的打印字符串方式,传首地址打印
co

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



