C++ 之 string类的模拟实现

这篇具有很好参考价值的文章主要介绍了C++ 之 string类的模拟实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++ 之 string类的模拟实现,c++,开发语言,经验分享,学习,其他,笔记

这学习我有三不学

昨天不学,因为昨天是个过去

明天不学,因为明天还是个未知数

今天不学,因为我们要活在当下,我就是玩嘿嘿~

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

目录

一、string类的模拟实现

1.成员函数(Member functions)

1.1 构造函数(constructor)

1.2 析构函数(destructor)

1.3 赋值拷贝函数(operator=)

2.迭代器(iterators)

3.容量(Capacity)

4.元素访问(Element access)

5.调节器(Modifiers)

6.字符操作(String operation)

7.成员常量(Member constant)npos实现

8.非成员函数重载(Non-member function overload)

二、完结撒❀


前言:

模拟string类的实现对于我们学习认识string类会有更加深刻的理解,还没学过string类的老铁建议可以先看学习一下我的上一篇博客讲解:C++ 之 string类 详细讲解,再来进行模拟实现。

一、string类的模拟实现

在上篇博客中讲解了string类的常用接口,这篇博客带大家模拟实现一下string类的一些常用接口。

string类查阅文档

我们根据上面文档所规划的接口分类为大家进行部分模拟实现,大家可以先简单看一下上面文档。

1.成员函数(Member functions)

1.1 构造函数(constructor)

● 无参构造函数 string() 实现:

string()
	//:_str(nullptr)
	:_str(new char[1])//不能赋空指针,因为直接c_str会出错
	, _size(0)
	, _capacity(0)
{
	_str[0] = '\0';
}

有参(字符串)构造函数 string(const char* str = "") 实现:

//string(const char* str = nullptr)错
//string(const char* str = '\0')错
string(const char* str = "")//常量字符串默认结尾含有\0
//该构造函数可以替代上面无参构造函数
	:_size(strlen(str))
{
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

● 拷贝构造函数 string (const string& s) 实现:
 

//s2(s1)
string (const string& s)
{
	_str = new char[s._capacity+1];//完成深拷贝,+1 存放\0使用
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

1.2 析构函数(destructor)

● ~string()实现:

~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

1.3 赋值拷贝函数(operator=)

● operator=实现:

	//s2 = s1
	string& operator=(const string& s)
	{
		char* tmp = new char[s._capacity + 1];//多开辟的1个空间用来存储\0
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;

		return *this;//支持连续赋值
	}

2.迭代器(iterators)

● begin实现:

在string类中迭代器中begin表示的就是字符串的首地址的指针,但并不是所有迭代器都是由指针来实现的。

这里使用迭代器的对象有两种,一种是const修饰的,一种是const没有修饰的,所以begin实现应有const修饰,和const不修饰两种:

typedef char* iterator;
typedef const char* const_iterator;

iterator begin()
{
	return _str;
}

const_iterator begin() const
{
	return _str;
}

● end实现:

同理,end实现也一样:

typedef char* iterator;
typedef const char* const_iterator;

iterator end()
{
	return _str + _size;
}

const_iterator end() const
{
	return _str + _size;
}

这里可以再说一下,范围for的实现也是基于迭代器begin和end所实现的,大家可以在汇编代码中就可以看到。

而对于const修饰的对象和const没有修饰的对象分别对应使用的范围for其内部实现也是const修饰的begin,end和const没有修饰的begin,end两种范围for。

3.容量(Capacity)

● size实现:

size_t size() const
{
	return _size;
}

● capacity实现:

size_t capacity() const
{
	return _capacity;
}

● resize实现:
由于resize函数的实现可能需要对数组进行扩容,所以我们先实现一下reserve函数:

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n+1];//多开1个位置给\0
		strcpy(tmp, _str);
		delete[] _str;//!!!
		_str = tmp;

		_capacity = n;
	}
}

注意:reserve只有在n大于当前有效空间是才会进行开辟,当n小于当前有效空间不会进行任何操作。

resize实现:

void resize(size n,const char* c)
{
    if(n>_size)
    {
        reserve(n);
        for(size i=size; i<n;i++)
        {
            _str[i] = c;
        }
        _str[n] = '\0';
        _size = n;
    }
    else
    {
        _str[n] = '\0'; 
        _size = n;
    }
}

● clear实现:

注意:clear只是清楚当前所存数据,而不是销毁空间

void clear()
{
	_size = 0;
	_str[0] = '\0';
}

4.元素访问(Element access)

● operator[]实现:

operator[]的实现功能就是访问string类里面字符串的字符所以实现并不复杂:
 

char& operator[](size_t pos)
{
	assert(pos < _size);

	return _str[pos];
}

const char& operator[](size_t pos) const
{
	assert(pos < _size);

	return _str[pos];
}

5.调节器(Modifiers)

● insert实现:

插入字符或字符串那么肯定会涉及到空间不够是否需要扩容的问题,解决了之后剩下的就是将插入位置之后的字符串向后移动插入字符或字符串大小的位置,为要插入的字符或字符串流出插入空间,之后再将字符或字符串插入即可:

void insert(size_t pos, char ch)
{
	assert(pos <= _size);

	if (_size == _capacity)
	{
		//扩容。。。reserve
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	//后移字符串方案1
	//int end = _size;
	//while (end >= (int)pos)//!!!一个运算符两边操作数类型不同的时候发生类型提升(范围小的像范围大的提升 这里有符号向无符号提升)
	//{
	//	_str[end + 1] = _str[end];
	//	--end;
	//}

	//后移字符串方案2
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	_size++;
}

void insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	size_t end = _size + len;
	while (pos < end - len + 1)
	{
		_str[end] = _str[end - len];
		--end;
	}

	strncpy(_str + pos, str, len);
	_size += len;
}

● append实现:
既然已经实现了insert,那么我们直接赋用insert实现append,push_back,operator+=即可:

	void append(const char* str)
	{
		//size_t len = strlen(str);
		//if (len + _size > _capacity)
		//{
		//	//扩容。。。
		//	reserve(_size + len);
		//}

		//strcpy(_str + _size, str);
		//_size += len;

		insert(_size, str);
	}

● push_back实现:

void push_back(const char ch)
{
	//if (_size == _capacity)
	//{
	//	//扩容。。。reserve
	//	reserve(_capacity == 0 ? 4 : _capacity * 2);
	//}

	//_str[_size] = ch;
	//_str[_size + 1] = '\0';
	//++_size;

	insert(_size, ch);
}

● operator+=实现:
 

string& operator+=(const char ch)
{
	push_back(ch);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}

● erase实现:

当len要清除的字符长度大于等于总字符长度减去pos起始位置时就要将pos起始位置后面的字符全都清除,即_str[pos] = '\0'即可。

	void erase(size_t pos = 0, size_t len = npos)
	{
		assert(pos < _size);
		
			if (pos == npos || pos >= _size - len)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
	}

6.字符操作(String operation)

● c_str实现:

const char* c_str() const
{
	return _str;
}

● find实现:

size_t find(const char c, size_t pos = 0) const
{
	assert(pos < _size);

	//从pos位置开始遍历字符串
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == c)
		{
			return i;
		}
	}
	return npos;
}

size_t find(const char* sub, size_t pos = 0) const
{
	assert(pos < _size);

	const char* p = strstr(_str+pos, sub);
	if (p)
	{
		return p - _str;//指针-指针为两指针之间的距离
	}
	return npos;
}

● substr实现:


与erase分析相似,当要截取的len长度的字符大于等于总字符长度_size减起始位置pos时,就要将pos后面的字符全部截取进行返回:

	string substr(size_t pos = 0, size_t len = npos) const
	{
		string sub;
		if (len >= _size - pos)
		{
			for (size_t i = pos; i < _size; i++)
			{
				sub += _str[i];
			}
		}
		else
		{
			for (size_t i = pos; i < pos + len; i++)
			{
				sub += _str[i];
			}
		}
		return sub;
	}

7.成员常量(Member constant)npos实现:

在标准库中,npos为静态全局变量,在npos文档中就有说明,其值应为size_t类型的-1。

private:
	char* _str = nullptr;
	size_t _size = 0;
	size_t _capacity = 0;

	public:
		static size_t npos;//类内声明
};

size_t string::npos = -1;//类外定义

8.非成员函数重载(Non-member function overload)

● swap实现:


在C++库中是有swap函数的,我们可以直接调用使用,但是对于自定义类型来说就比如string类,其直接调用库中的swap函数进行交换的话会调用3次拷贝构造+1次析构(库中的swap实现方式是创建一个新的临时变量tmp进行两个值的交换),这样消耗会很大,所以对于自定义类型我们不推荐直接调用库里面的swap函数。

那么我们该如何实现swap函数呢?我们可以直接对自定义类型里面的内置成员变量进行交换即可满足两者自定义类型的交换,将swap函数定义为非成员函数是因为要满足swap(s1,s2);交换的格式。

内部成员函数:

	void swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

非成员函数(全局函数):

void swap(string& s1, string& s2)
{
	s1.swap(s2);//直接调用成员函数即可
}

● operator<<实现:

ostream& operator<<(ostream& out, const string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;//实现连续打印
}

● operator>>实现:

istream& operator>>(istream& in, string& s)
{
	s.clear();//输入变量值之前需要将变量原先所存的值给清除
	char ch;
	ch = in.get();//可以获取空格' ',cin不支持获取空格

	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		ch = in.get();
	}

	return in;//实现连续赋值
}

● getline实现:

istream& getline(istream& in, string& s)
{
	s.clear();

	char c;
	c = in.get();
	//栈区开辟空间,栈区开辟空间比堆区开辟空间高
	char ch[128];//提高效率
	size_t i = 0;

	while (c != '\n')
	{
		ch[i++] = c;

		if (i == 127)
		{
			ch[127] = '\0';
			s += ch;
			i = 0;
		}
        c = in.get();
	}

	if (i > 0)
	{
		ch[i] = '\0';
		s += ch;
	}
	return in;
}

上面所实现的都是string类的常用的一些接口,其他一些没有实现的大家感兴趣的话可以查阅其他资料自行模拟实现一下

二、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤

C++ 之 string类的模拟实现,c++,开发语言,经验分享,学习,其他,笔记文章来源地址https://www.toymoban.com/news/detail-860111.html

到了这里,关于C++ 之 string类的模拟实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】学习string类的模拟实现

    前面已经学习了string类的用法,这篇文章将更深入的学习string类,了解string类的底层是怎么实现的。当然,这里只是模拟一些常用的,不常用的可以看文档学习。 我们一共创建两个文件,一个是test.cpp文件,用于测试;另一个是string.h文件,用于声明和定义要模拟的string类。

    2024年02月03日
    浏览(53)
  • 【c++】string类的使用及模拟实现

    我们先了解一下什么是OOP思想 OOP思想,即面向对象编程(Object-Oriented Programming)的核心思想,主要包括“抽象”、“封装”、“继承”和“多态”四个方面。 抽象:抽象是忽略一个主题中与当前目标无关的那些方面,以便充分地注意与当前目标有关的方面。抽象并不打算了

    2024年04月11日
    浏览(35)
  • STL中的string类的模拟实现【C++】

    构造函数设置为缺省参数,若不传入参数,则默认构造为空字符串。字符串的初始大小和容量均设置为传入C字符串的长度(不包括’\\0’) 在模拟实现拷贝构造函数前,我们应该首先了解深浅拷贝: 浅拷贝:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一

    2024年02月15日
    浏览(42)
  • 【C++初阶】第八站:string类的模拟实现

    目录 string类的模拟实现 经典的string类问题 浅拷贝 深拷贝 写时拷贝(了解) 构造函数 string的全缺省的构造函数: string的拷贝构造函数 传统写法 现代写法 string的赋值重载函数 传统写法 现代写法 string的无参构造函数: 遍历函数 operator[ ] 迭代器 迭代器的底层实现begin和end:

    2024年04月28日
    浏览(40)
  • 【C++初阶】STL详解(二)string类的模拟实现

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 注:为了防止与标准库当中的string类产生命名冲

    2024年02月05日
    浏览(58)
  • 【C++】深度剖析string类的底层结构及其模拟实现

    在上两篇中,我们已经学习了string类的一个使用,并且做了一些相关的OJ练习,相信大家现在对于string的使用已经没什么问题了。 那我们这篇文章呢,就来带大家对string进行一个模拟实现,这篇文章过后,有些地方大家或许就可以理解的更深刻一点。 那通过之前文章的学习我

    2023年04月17日
    浏览(107)
  • 【C++练级之路】【Lv.6】【STL】string类的模拟实现

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C语言,共同探讨和进步哦! 学习专栏 : 《进击的C++》 关于 STL容器 的学习,我会采用 模拟实现 的方式,以此来更加清楚地了解其 底层原理和整体架构 。而string类更是有100多个接口函数,所以模拟实现的时候只会调重点和

    2024年01月18日
    浏览(50)
  • [C++] string类的介绍与构造的模拟实现,进来看吧,里面有空调

    C语言中,字符串是以’\\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。 因此C++中,为了让我们更简单、方便

    2024年02月12日
    浏览(39)
  • 【C++】:STL中的string类的增删查改的底层模拟实现

    本篇博客仅仅实现存储字符(串)的string 同时由于C++string库设计的不合理,我仅实现一些最常见的增删查改接口 接下来给出的接口都是基于以下框架: C++string标准库中,无参构造并不是空间为0,直接置为空指针 而是开一个字节,并存放‘\\0’ C++中支持无参构造一个对象后,直

    2024年02月05日
    浏览(53)
  • string类的模拟实现

    上一篇博客我们对string类函数进行了讲解,今天我们就对string类进行模拟实现,以便于大家更加深入地了解string类函数的应用 由于C++的库里面本身就有一个string类,所以我们为了不让编译器混淆视听,我们可以首先将我们自己模拟实现的string类放入一个我们自己定义的命名空

    2024年01月21日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包