7.【CPP】String类

一.汉字的编码

我们知道计算机存储英文字母,标点,数字用的是ascall码,128种用一个字节表示绰绰有余。而汉字远远不止128种,因此汉字需要两个字节表示

1.gbk编码中汉字占两个字节。
2.utf-8中,一个汉字占三个字节。

> UTF规定:如果一个符号只占一个字节,那么这个8位字节的第一位就为0。如果为两个字节,那么规定第一个字节的前两位都为1,然后第一个字节的第三位为0,第二个字节的前两位为10,然后如果是三个字节的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10。按照这样的算法去思考一个中文字符的UTF-8是怎么表示的:一个中文字符需要两个字节来表示,两个字节一共是16位,那么UTF-8下,两个字节是不够的,因为两个字节下,第一个字节已经占据了三位:110,然后剩余的一个字节占据了两位:10,现在就只剩下8位,与Unicode下的两个字节,16位去表示任意一个字符是相悖的,也就是Unicode下的16位减去UTF-8下的8位=8位,刚好差了一个字节的空间,所以就使用三个字节去表示非ANSI字符:三个字节下,一共是24位,第一个字节头四位是:1110,后两个字节的前两位都是:10,那么24位-8位=16位,刚好两个字节去表示Unicode下的任意一个非ANSI字符

7.【CPP】String类_第1张图片

二.string类

参考string官方文档

1.扩容

7.【CPP】String类_第2张图片

这里resize和reserve都是在string对象后开指定大小的可用空间,而不是把它的容量设置为指定大小。
resize改变了size大小,而reserve不改变.
resize空间小于size大小时会删除元素,reserve空间小于size时不会改变容量也不删除元素。

2.小试牛刀

2.1把字符串转化为整数

迭代器实现

#include 
class Solution {
  public:
    int StrToInt(string str) {
        int ans = 0;
        int i=0;
        for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit) {
            
            if (isdigit(*rit)) {
                ans=ans+((*rit)-48)*pow(10,i++);
                continue;
            }
            if (*rit == '+')
                break;
            else if (*rit == '-')
                ans *= -1;
            else
                return 0;
        }
        return ans;
    }
};

2.2.Leetcode415.字符串相加

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1=num1.size()-1,end2=num2.size()-1;
        int carry=0;
        string ans;
        //提前扩一波容
        ans.reserve(num1.size()>num2.size()?num1.size()+1:num2.size()+1);
        while(end1>=0||end2>=0)
        {
            int val1=end1>=0?num1[end1]-'0':0;
            int val2=end2>=0?num2[end2]-'0':0;
            int ret=val1+val2+carry;
            carry=ret/10;
            ans+=ret%10+'0';
            --end1;
            --end2;
        }
        if(carry)
            ans+='1';
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

7.【CPP】String类_第3张图片

这里我们用数学中用竖式计算加法的方法,用carry表示进位,从后往前一步一步加到ans中,同时注意细节处理,最后反转一下ans得到答案。

3.find+replace(用xx替换xx)

7.【CPP】String类_第4张图片
这里我们用空格替换“/”,如果用多个字符替换呢?那么上述方法就显得很低效,因为不仅开空间还要往后挪数据。解决方法:以空间换时间,重新定义一个空字符,遍历两遍老字符。第一遍开空间,第二遍往空字符加数据。如下图
7.【CPP】String类_第5张图片

4.string类的swap和算法的swap

7.【CPP】String类_第6张图片
我们可以看到string的swap只需要改变指针指向就可以了,而std库里的需要创建一个临时对象还要拷贝赋值完成交换。

5.c_str()

将string转化为c语言能够识别的const char*,如下打开文件并读取
7.【CPP】String类_第7张图片

三.string类的写时拷贝(copy-on-write)

1.看下面这段代码

#include
#include

using namespace std;
int main()
{
  string s1("hello,world");
  string s2(s1);
  printf("%x\n",s1.c_str());
  printf("%x\n",s2.c_str());

  s1[2]='m';
  printf("%x\n",s1.c_str());
  printf("%x\n",s2.c_str());

  s2[2]='m';
  printf("%x\n",s1.c_str());
  printf("%x\n",s2.c_str());
  return 0;
}

下面代码在g++下运行结果
7.【CPP】String类_第8张图片
我们发现前面不修改s1和s2时两者地址是一样的,而修改s1中的字符时,操作系统此时才会给s1开辟一块独立的新空间把数据拷贝过去并修改。这就是所谓的写时拷贝技术。

2.简要理解如何实现?

每当我们为string分配内存时,我们总是要多分配一个空间用来存放这个引用计数的值,只要发生拷贝构造可是赋值时,这个内存的值就会加一。而在内容修改时,string类为查看这个引用计数是否为0,如果不为零,表示有人在共享这块内存,那么自己需要先做一份拷贝,然后把引用计数减去一,再把数据拷贝过来。

四.string类的模拟实现

class string {
	public:
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str+_size;
		}

		string(const char* str = "") :
			_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1+1];
			strcpy(_str, str);
			
		}

		
		/*string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}*/

//现代写法
		void swap(string& tmp)
		{
			::swap(_str, tmp._str);
			::swap(_size, tmp._size);
			::swap(_capacity, tmp._capacity);
		}
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
			_str[_capacity + 1]++;
		}


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

		//实现赋值操作
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				/*delete[] _str;
				_str = new char[strlen(s._str) + 1];
				strcpy(_str, s._str);*/
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp, s._str);

				delete[] _str;

				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

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

		size_t size()const
		{
			return _size;
		}

		size_t capacity()const
		{
			return _capacity;
		}

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

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;

				_str = tmp;
				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 0 : _capacity * 2);
			}

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

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

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

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

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			/*size_t end = _size;
			while (end >= pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}*/

			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* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end + len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
			}
		}

		size_t find(char ch,size_t pos = 0)const
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)const
		{
			assert(sub);
			assert(pos < _size);
			const char* ptr = strstr(_str + pos, sub);
			if (ptr == NULL)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		string substr(size_t pos, size_t len = npos)const
		{
			assert(pos < _size);
			size_t reallen = len;
			if (len == npos || pos + len > _size)
			{
				reallen = _size - pos;
			}

			string sub;
			for (size_t i = 0; i < reallen; i++)
			{
				sub += _str[pos + i];
			}
			return sub;
		}


	private:
		char* _str;
		size_t _size;
		size_t _capacity;


	public:
		static size_t npos;
	};
	size_t string::npos = -1;

	istream& operator>>(istream& in, string& s)
	{
		char ch;
		ch = in.get();

		const size_t N = 32;
		char buff[N];
		size_t i = 0;

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i=N-1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		buff[i] = '\0';
		s += buff;
	}

你可能感兴趣的:(CPP,算法,c++,数据结构,开发语言)