简单复写C++string类

主要写增删查改和几种常见的构造方式:


定义先确定string类中成员变量:

private:
		char* _str;
		size_t _size;//实际空间
		size_t _capacity; //有效空间,真实容量没算\0;
		static const size_t npos;//位置
	};

1.1第一种构造方式 string(const char *s) :

  	//s1("HAOHAO")
		string(const char* str)
			:
			_size(strlen(str))//开空间初始化
			, _capacity(_size)
			
		{
			_str=new char[_capacity + 1];
			strcpy(_str, str);
		
		}

1.2.第二种构造方式拷贝构造://3.string(const string & str,string size_type n = npos):

这里需要用深拷贝,浅拷贝会报错,浅拷贝并没有直接开辟空间而是指针指向了被拷贝的空间,看起来没什么问题,但是程序结束时会报错,因为结束时会调用析构函数,按照下图所示s2会先调用析构函数,之后s1也会调用析构函数,此时s1的空间已经被s2释放了所以程序报错;

string s1("HAOHAO");
string s2=(s1);

在这里插入图片描述

深拷贝又有好几种写法,分为古代和现代两种写法

		//深拷贝古代写法:啥都亲力亲为
       string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
	//	深拷贝现代写法:找别人干活
		string(const string& s) 
		:_str(nullptr)
		{
			string tmp(s._str);//这里s._str的类型是char*所以调用的是我们自己写的第一种构造方式
	//这个swap是我们自己写的函数,后面重载操作符 =的时候会说为啥要自己写
			this->swap(tmp);
		}

1.3.重载操作符 =:

这也分了古代和现代两种写法,现代中用了我们自定义的swap函数,如果用swap(_str,s.str)这么写只是把指针_str和s.str这两个指针互换了,this的容量并没有交换无法全部拷贝到

		//s1=s3;古代写法
		string& operator=(const string& s) {
			//释放掉原空间,避免浪费,可能s1很大s3很小
			if (this!= &s) {//s3=s3这种情况
				char* tmp = new char(s._size);//先开辟空间再释放
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;//传的地址
				_size = s._size;
				_capacity = s._capacity;
				
			}
			return *this;
		}
		//现代写法
		string & operator=(const string &s) {
		
			if (this != &s) {
				string tmp(s);
				//普通的swap(_str,tmp_str)只是交换了_str指针的方向但是string类里size和capacity还跟以前一样,会出问题
				this->swap(tmp);
				cout << "交换后" << endl;

				cout << this->_str << endl;
			\
			}
			return *this;
		
		}
		void swap(string& s) {
		
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);

		
		}

1.4.重载运算符[ ]、s._str()、size()函数:

char& operator[](size_t pos) {
			assert(pos < _size);//防止越界直接断死
			return _str[pos];
		
		}
		const char* c_str() {
		
			return _str;
		
		}
		size_t size()const {
			return _size;

	}

1.5.查找单个字符和字符串:

		//查找单个字符
		size_t find(const char ch) {
		
			for (size_t i = 0; i < _size; i++) {
			
				if (ch == _str[i]) {
				
					return i;
				}
			
			}
			return npos;//没有找到返回npos
		
		}
		//查找字符串
		size_t find(const char *s,size_t pos=0) {

			for (size_t i = 0; i < _size; i++) {
//:strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
				const char* ptr = strstr(_str + pos, s);
				if (ptr == nullptr) {
				
					return npos;
				}
				else {
				
					return ptr - _str;
				}
			}
			return npos;//没有找到返回npos

		}

1.6.reserve()和resize()函数:

扩容reserve和resize: reserve的逻辑是先把内容拷贝下来,存在tmp中再把原来的空间给删掉;resize的容量可能会被变小,变小的话直接在n的位置加上’\0’,然后改变_size,如果变大又分了两种情况(1)n比_size大比_capacity小,(2)n直接比capacity大。
第一种情况直接在比 下标size 大的填上‘\0’占位;
第二种情况先扩容再占位;
在这里插入图片描述

void reserve(size_t n) {//改变capacity n比原来的那么就变成n 比原来小就不变
			if (n > _capacity) {
				char* tmp = new char[n + 1];//\0
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			
			}
		}
		void resize(size_t n, char ch= '\0') {
			if (n <= _size) {//resize跟reserve不一样它如果resize一个比它之前size小的数会缩容量的,最后一个位置填‘\0’
				_str[n] = '\0';
				_size = n;
			}
			else {
				if (n > _capacity) {
				
					reserve(n);
				}
				memset(_str + _size, ch, n - _size);//扩容后把空间填充从size开始
				_str[n] = '\0';
				_size = n;
			
			}
		}

1.7.push_back()函数:

	void push_back(char ch) {
//判断是否为空和是否已经满了,如果本身没有空间扩容四个字节,如果本身有空间但是满了直接扩容两倍
			if (_size == _capacity) {
				reserve(_capacity==0?4: _capacity *2);

			}

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

		}

1.8.inset()函数:

指定位置插入字符或字符串:先判断容量是否够不够就扩,然后把从最后一个字符往后移,直到pos位置为空,end一定要在最后一个字符的后面一个位置也就是‘\0’的位置,如果end=_size,判断条件while (end>=(pos) ,如果此时在第0个位置插入end最后会变成-1程序歇火;

插入字符串也是一样的也是从最后面开始移动但是end要在size+len(插入字符串长度)的位置,直到找到pos

在这里插入图片描述

//插入字符
		string& insert(size_t pos, char ch) {
		
			assert(pos <= _size);
			//判断容量是否满了
			if (_size == _capacity) {
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			
			}
			//这么写 在第0个位置插入时会报错,end变为-1直接歇火size_t类型不能为负数,转为int的时候while (end>=pos)这个判断还会出问题要
			// while (end>=(int)pos)
			//size_t  end = _size;
			往后移直到第pos个位置为空
			//while (end>=pos) {
			//	_str[end + 1] = _str[end];
			//	--end;
			//}
			//直接 HAO\0,把end放在斜杠0后面
			size_t end = _size+1;
			while (end > pos) {
			
				_str[end] = _str[end - 1];

				end--;
			
			}
			_str[pos] = ch;
			++ _size;
			return *this;
		}
		//插入字符串
		string& insert(size_t pos,const char *ch) {


			assert(pos <= _size);
			//先看容量够不够
			size_t len = strlen(ch);
			if (_size + len > _capacity) {
			
				reserve(_size+len);
			
			}
			size_t end = _size + len;
			while (end > =pos+len) {

				_str[end] = _str[end - len];

				end--;

			}
			
			strncpy(_str, ch, len);
			_size+=len;
			return *this;

		}

1.9 append()函数:

在末尾插入字符串扩容时要结合实际情况扩容,还有一种简便写法直接调用上面写的insert函数

void append(const char* ch) {
			//扩容不能单纯的扩两倍可能不够万一要结合实际
			size_t len = strlen(ch);
			if (_size + len > _capacity) {
				this->reserve(_size+len);
			}
			strcpy(_str + _size, ch);//从上一个单词的末尾插
			
		}
		void append(const char* ch) {
		
			insert(_size, ch);
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值