✨个人主页:熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】
目录
[2. 构造函数的实现](#2. 构造函数的实现)
上两弹我们学习了如何使用库中的string类,但是我们作为程序员不仅仅只是会用,还得了解其中的底层原理,因此接下来的内容将一步步的讲解如何模拟实现。
1、string结构的创建
实现有关string的函数功能,前提得有一个string这样的类,因此第一步是创建一个string类,但是为了与库函数的string进行区分 ,此时可以将类创建在命名空间中。那么string的成员变量 有哪些呢?首先需要一个存放字符串地址的指针 ,存储有效数据个数的变量 以及存放容量的变量。
注意:建议将成员变量放在私有权限中,更好的保护数据。
namespace lin
{
class string
{
public:
//以下为成员函数
private://保护成员变量,用private
char* _str;//存放字符串指针
int _size;//字符串有效数据个数
int _capacity;//字符串容量
};
}
2. 构造函数的实现
根据前面构造函数的使用我们可以知道,构造函数有很多个,但是有一些思想基本是一致的,因此博主只对几个常见的构造函数和析构函数进行模拟实现了。
1.1、无参构造函数
根据前面的实现我们知道,无参构造函数时指有效数据个数为0 ,但是**字符串中是存放了'\0'**的,因此我们需要开辟一个字节的空间。
string()
//初始化列表
:_str(new char)//给_str开辟一个字节空间
, _size(0)//初始化有效数据为0
, _capacity(1)//容量为1
{
_str[0] = '\0';//将开辟的空间初始化值为'\0'
}
我们模拟实现了这个构造函数,但是我们需要通过测试才知道是否正确,因此博主推荐的方法是使用多文件,在test.cpp文件中进行对函数的测试,string.h的文件中进行实现函数。
调试的测试结果如下:
1.2、有参构造函数
1、参数为C语言格式的字符串拷贝函数
拷贝函数那么需要将类中的所有成员变量均拷贝过去,有效数据个数为传参的字符串长度,容量也直接等于有效字符串长度即可。
string(const char* str)
:_str(new char[strlen(str)+1])//开辟字符串长度+1字节的空间
,_size(strlen(str))//大小为字符串长度
,_capacity(strlen(str))//容量为字符串长度
{
strcpy(_str, str);//将字符串的内容拷贝到_str中
}
根据我们的直觉,一般就会直接写以上的代码,但是此代码似乎很繁琐,求了三次字符串长度,我们可不可以进行优化一些呢?
博主的答案是当然可以,我们构造函数的初始化不一定要在初始化列表 ,也是可以在函数体进行初始化的,还有此代码跟上面的无参构造函数都会开辟新的空间,以及拷贝内容,那么能不能通过缺省参数修改成一个公用的函数呢?
优化一(单独版本):
string(const char* str)
:_size(strlen(str))//大小为字符串长度
{
_capacity = _size;
_str = new char[_capacity + 1];//开辟空间比字符串长度大1,存放\0
strcpy(_str, str);//拷贝字符串
}
优化二(合并版本):
string(const char* str = "")
:_size(strlen(str))
{
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
2、参数为类对象的拷贝构造函数
此处为了能够满足多文件实现思想,将有效数据个数以及容量封装成一个函数,那么类外也可以访问数据个数以及容量,因此代码如下。
string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s.size();
_capacity = s.capacity();
}
size_t capacity() const
{
return _capacity;//类内封装获取容量函数
}
size_t size() const
{
return _size;//类内封装有效数据个数函数
}
1.3、析构函数
前面构造函数我们是手动开辟的空间,因此需要释放空间。
~string()
{
delete[] _str;//释放空间
_str = nullptr;//手动置空,防止野指针
_size = _capacity = 0;//大小置0
}
3、赋值操作符重载的实现
需要赋值的类的有效数据个数有三种情况:
1、为空串(空间不够)
2、比拷贝的有效数据个数少(空间不够)
3、比拷贝的有效数据个数多(空间足够)
因此此处选择一个通用的解决办法,直接开辟一个新的空间,将原空间的数据拷贝给新空间,然后销毁原空间。
string& operator= (const string& s)
{
char* tmp = new char[s._capacity + 1];//开辟一个新空间,大小为s的容量+1
strcpy(tmp, _str);//将原空间的数据拷贝给新空间
delete[] _str;//释放原空间
//下面拷贝类中的成员变量数据
_str = tmp;
_size = s._size;
_capacity = s._capacity;
return *this;//返回string类
}
4、容量操作的实现
注意:返回字符串的长度和容量是不会修改值的,因此可以用const修饰。
容量的常见函数如下:
1、size_t size() const;
//字符串有效数据个数(字符串长度)
2、size_t length() const;
//字符串有效数据个数(字符串长度)
3、size_t capacity() const;
//字符串容量
4、void resize(size_t n);
//扩大字符空间至n字节,大于原来大小则用\0填充
5、void reserve(size_t n = 0);
//扩大容量
6、void clear();
//清空字符串
7、bool empty() const;
//判断是否为空串
size_t capacity() const
{
return _capacity;
}
size_t size() const
{
return _size;
}
size_t length() const
{
return _size;//长度和大小相等的,直接返回大小即可
}
void clear()
{
_size = 0;//将有效数据个数置0
_str[_size] = '\0';//第0个位置给\0
}
void reserve(size_t n)
{
if (n > _capacity)//n大于原来容量才需要扩容
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)//n小于原来大小,直接给n位置\0
{
_str[n] = '\0';
_size = n;//大小修改为n
}
else
{
reserve(n);//先扩容再赋值
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
_size = n;
}
}
bool empty() const
{
return _size == 0;//大小为0则为空,否则不为空
}
5、元素访问的实现
1、char& operator[] (size_t pos);
//下标访问元素,可修改值
const char& operator[] (size_t pos) const;//下标访问元素,不可修改值
2、char& at (size_t pos);//函数访问元素,可修改值
const char& at (size_t pos) const;
//函数访问元素,不可修改值
3、char& back();//函数访问尾元素,可修改值
const char& back() const;
//函数访问尾元素,不可修改值
4、char& front();//函数访问头元素,可修改值
const char& front() const;
//函数访问头元素,不可修改值
char& operator[] (size_t pos)
{
assert(pos < _size);//断言,pos小于_size则没问题,否则报错
return _str[pos];//返回pos位置元素
}
const char& operator[] (size_t pos) const
{
assert(pos < _size);
return (const char&)_str[pos];//强转成const char&类型
}
char& back()
{
return _str[_size - 1];
}
const char& back() const
{
return (const char&)_str[_size - 1];
}
char& front()
{
return _str[0];
}
const char& front() const
{
return (const char&)_str[0];
}
总结
本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!