string的模拟实现

1.简单实现string

我们先实现一个简单的string,只考虑资源管理深浅拷贝问题
暂且不考虑增删查改,增删查改版本在下面目录会有源代码
那么实现string先把基本的写了些,比如构造析构函数等等
因为库里面有个string所以这里有二种做法,一种是命名空间就是我下面那种,另一种把我们写的类把小写string改成大写String这样来区分来
代码

#pragma once


namespace str
{
   
	// 先实现一个简单的string,只考虑资源管理深浅拷贝问题
	// 暂且不考虑增删查改
	class string
	{
   
	public:
		string(const char* str)
			:_str(new char[strlen(str) + 1])//这个加1是留给'\0'的
		{
   
			strcpy(_str, str);
		}
		~string()
		{
   
			if (_str)
			{
   
				delete[] _str;
			}
		}
	private:
		char* _str;
	};
}

这上面有个小问题,就是在主函数记得包我们写的这个头文件,要不然strlen它会找不到,为什么包含了这个就会找到呢?
因为主函数里面保含了#include这些库,执行文件的时候会预处理,预处理里面有头文件展开,而前面的文件展开了,那么就有定义strlen了

没保含头文件的执行结果
图片
在这里插入图片描述

包含的
在这里插入图片描述
除了这个还有一个问题,如果没有加
#define _CRT_SECURE_NO_WARNINGS 这一串有的编译器是编译不过的,比如我的vs2022就会有下面图片的报错
在这里插入图片描述
这个报错其实是认为这个函数不安全所以报错,
而#define _CRT_SECURE_NO_WARNINGS是屏蔽这个报错

写完了析构和构造,那么我们就要实现string的基本用法了,比如用string创建一个字符串再打印出来,我们发现直接打印是打印不出来的
代码

#pragma once


namespace str
{
   
	// 先实现一个简单的string,只考虑资源管理深浅拷贝问题
	// 暂且不考虑增删查改
	class string
	{
   
	public:
		string(const char* str)
			:_str(new char[strlen(str) + 1])//这个加1是留给'\0'的
		{
   
			strcpy(_str, str);
		}
		~string()
		{
   
			if (_str)
			{
   
				delete[] _str;
			}
		}
		const char* c_str() const
		{
   
			return _str;
		}
	private:
		char* _str;
	};
}

打印使用方式

void test1()
{
   
	str::string s1("hello world");
	cout << s1.c_str() << endl;
}

那我们现在再做其他功能,简单修改字符串和打印每个字符用空格分割
代码

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

			return _str[pos];
		}

		size_t size()
		{
   
			return strlen(_str);
		}
void test1()
{
   
	str::string s1("hello world");
	cout << s1.c_str() << endl;
	s1[0] = 'x';
	cout << s1.c_str() << endl;
	for (size_t i = 0; i < s1.size(); i++)
	{
   
		cout << s1[i] << " ";
	}
}

打印结果
在这里插入图片描述
接下来我们复制字符串
代码

void test2()
{
   
	str::string s1("hello world");
	str::string s2(s1);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
}

运行结果
在这里插入图片描述
可以看到打印结果出来了,但是报错了这是怎么回事?
我们来调试一下
在这里插入图片描述
可以看到s1和s2指向的是同一个位置,而这里有问题的是析构了二次,还有就是其中一个改变其他也会改变,而会造成这样的有个专属的名字叫浅拷贝
那么这时候我们就要用深拷贝了
深拷贝的意思是有一样大的空间一样的值,但是用的不是同一个空间
代码

		//s2(s1) s1是s s2是this 指针
		string(const string& s)
			:_str(new char[strlen(s._str) + 1])//这个加1是留给'\0'的
		{
   
			strcpy(_str, s._str);
		}

这样就不会浅拷贝的问题了

接下来来实现赋值
这里实现赋值一样有和之前一样的问题析构二次,还有就是内存问题就是内存太大或者不够越界,还有可能自己和自己赋值,自己和自己赋值就直接返回自己就好了
写法
先释放要被赋值的空间,拷贝赋值的空间大小给被赋值的空间,再把内容拷贝过去
代码

		//s1=s3
		string& operator=(const string& s)
		{
   
			if (this != &s)
			{
   
				delete[] _str;
				_str = new char[strlen(s._str) + 1];
				strcpy(_str, s._str);
			}

			return *this;
		}

不过这种代码有个问题就是,如果拷贝失败了怎么办?
因为它是先释放了再拷贝的
更好的版本

		//s1=s3
		string& operator=(const string& s)
		{
   
			if (this != &s)
			{
   
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp,s._str);
				delete[] _str;
				_str = tmp;
			}

			return *this;
		}

使用场景代码

void test3()
{
   
	str::string s1("hello wprld");
	str::string s2(s1);
	str::string s3("111111");
	s1 = s3;
	cout << s1.c_str() << endl;

}

2.考虑增删查改的string模拟实现

因为这里是要考虑增删查改的了,而插入肯定不能一个一个插入,所以
成员要加上size,capacity之类的
因为有些部分比较简单所以就直接放上代码(代码会有注释的)如果不知道里面的函数有什么用可以去翻翻我上期写的string讲解也可以在评论区问我,或者不知道函数怎么实现的可以在评论区问我
string模拟实现代码

#pragma once

#include<assert.h>

namespace str
{
   
	// 先实现一个简单的string,只考虑资源管理深浅拷贝问题
	// 暂且不考虑增删查改
	class string
	{
   
	public:

		typedef char* iterator;

		typedef const char* const_iterator;


		iterator begin() const
		{
   
			return _str;
		}

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

		iterator begin()
		{
   
			return _str;
		}

		iterator end()
		{
   
			return _str+_size;
		}

		string(const char* str="")//""这个就是默认'\0'
			:_size(strlen(str))
			,_capacity(_size)
		{
   
			_str = new char[_capacity + 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值