admin管理员组

文章数量:1794759

【C++】string类 (模拟实现详解 上)

        我们不仅要会使用strng的接口,还要模拟实现,更深地理解strng的底层逻辑。这里我们最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数这些比较核心的接口。

1.准备工作

        我们依旧采用声明和定义分离的方式模拟实现string,跟之前模拟实现Stack、顺序表那些是同样的操作,建三个文件,一个头文件string.h,两个源文件test.cpp和string.cpp。

string.h中用命名空间分隔一下,因为c++库里面也有string,避免冲突。string.h里面写一些会用到的头文件,一个string类,string类的成员变量都是老朋友了,size和capacity也是介绍过的,和模拟实现顺序表差不多的,前面的博文说过,string可以认为是char类型的顺序表。

代码语言:javascript代码运行次数:0运行复制
#include <iostream>
#include <assert.h>
using namespace std;

namespace lyj //用命名空间与库里的string分隔开
{
	class string
	{
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

2.基础接口

短小而且频繁被调用的函数,就直接放在string类里面,就不做声明和定义分离了。

2.1 无参构造

还是写在string.h里的string类,作为string类的成员函数。

代码语言:javascript代码运行次数:0运行复制
class string
{
public:
	string() //无参构造
		:_str(new char[1]{'\0'}) //不能是nullptr
		,_size(0)
		,_capacity(0)
	{}
private:
	char* _str;
	size_t _size;
	size_t _capacity;
};

无参构造走初始化列表,一个一个初始化,不传参_size和_capacity都是0,但是_str不可以为nullptr,这里要开辟一个空间,初始化为'\0'。

2.2 带参构造

写在string类里面。带参构造就不要走初始化列表初始化了,因为不是很方便,我们就在函数体里实现。

代码语言:javascript代码运行次数:0运行复制
class string
{
public:
	string() //无参构造
		:_str(new char[1]{'\0'})
		,_size(0)
		,_capacity(0)
	{}
	string(const char* str) //带参构造
	{
		_size = strlen(str);
		_capacity = _size; //capacity大小不包括\0
		_str = new char[_capacity + 1]; //开空间时多开一个
		strcpy(_str, str);//拷贝 
	}
private:
	char* _str;
	size_t _size;
	size_t _capacity;
};

传参的时候_size就是传过来的str的大小,_capacity也初始化的和size一样大,切记,_capacity大小是不包括'\0'的,但是我们开空间的时候要多开一个,所以new char的大小是_capacity+1。空间开好之后就把str的数据拷贝到_str去,就初始化好了。

test.cpp中测试一下带参构造和无参构造。

代码语言:javascript代码运行次数:0运行复制
#include "string.h"

namespace lyj //命名空间保持一致
{
	void test1(void)
	{
		string s1; //不传参
		string s2("hello world");//传参
	}
}

int main()
{
	lyj::test1(); //指定命名空间调用函数

	return 0;
}

2.3 无参构造和带参构造结合

两种构造函数可以结合成一个构造函数,如下。

代码语言:javascript代码运行次数:0运行复制
string(const char* str = "") //给缺省值,什么都没有的字符串
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);//拷贝 
}

什么都没有的字符串自带\0,strlen求的是\0之前的字符长度,如果不传参,strlen(str)的结果就是缺省值的空字符串大小,为0, _capacity = _size = 0,new的大小为1;如果传参就是带参构造的结果。

test.cpp中测试一下这个结合的构造函数。

代码语言:javascript代码运行次数:0运行复制
#include "string.h"

namespace lyj //命名空间保持一致
{
	void test1(void)
	{
		string s1; //不传参
		string s2("hello world");//传参
	}
}

int main()
{
	lyj::test1(); //指定命名空间调用函数

	return 0;
}

结果是正确的。 

2.3 析构函数

写在string类里面。

代码语言:javascript代码运行次数:0运行复制
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

 析构函数没啥多说的。

2.5 c_str

写在string类里面。这个接口也算是打印函数,我们还没实现流插入流提取函数,暂时用这个。

代码语言:javascript代码运行次数:0运行复制
const char* c_str()
{
	return _str;
}

test.cpp中测试一下。

代码语言:javascript代码运行次数:0运行复制
namespace lyj //命名空间保持一致
{
	void test1(void)
	{
		string s1; //不传参
		string s2("hello world");//传参
        //打印
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
	}
}

int main()
{
	lyj::test1(); //指定命名空间调用函数

	return 0;
}

2.6 size 、capacity 和 operator[]

都写在string类里面。

代码语言:javascript代码运行次数:0运行复制
size_t size() const
{
	return _size;
}
size_t capacity() const
{
	return _capacity;
}

operator提供两个版本,普通对象和const对象。 普通对象返回引用,可以修改,const对象返回const引用,不可修改。

代码语言:javascript代码运行次数:0运行复制
char& operator[](size_t pos) //普通对象
{
	assert(pos < _size);//断言,防止越界
	return _str[pos];
}
const char& operator[](size_t pos) const //const对象
{
	assert(pos < _size);//断言,防止越界
	return _str[pos];
}

3.迭代器和范围for

先实现迭代器。

代码语言:javascript代码运行次数:0运行复制
typedef char* iterator; //给char*换个名字叫iteraator
iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;//_str + _size是'\0'的位置
}

但是不可以认为迭代器就是指针,没有这么简单。 只能在string里面用指针这样实现一下。

test.cpp里测试。

代码语言:javascript代码运行次数:0运行复制
namespace lyj //命名空间保持一致
{
	void test1(void)
	{
		string s1; //不传参
		string s2("hello world");//传参
		//迭代器遍历
		string::iterator it = s2.begin();
		while (it != s2.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

	}
}

int main()
{
	lyj::test1(); //指定命名空间调用函数

	return 0;
}

 范围for可以直接用,因为我们说过,范围for其实底层就是迭代器。

如果我们现在把迭代器的实现注释掉,范围for也不能用。

除了普通迭代器,还有const迭代器。

代码语言:javascript代码运行次数:0运行复制
typedef const char* const_iterator; //给const char*换个名字叫const_iteraator
const_iterator begin() const
{
	return _str;
}
const_iterator end() const
{
	return _str + _size;
}

到这我们就大概实现了string的大概框架,增删查改的接口我们下次再说,本篇就到这里,拜拜~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024-10-20,如有侵权请联系 cloudcommunity@tencent 删除string对象函数接口c++

本文标签: Cstring类 (模拟实现详解 上)