• C++STL【string】下模拟实现string


    string类的接口总览

    namespace cl
    {
    	//模拟实现string类
    	class string
    	{
    	public:
    		typedef char* iterator;
    		typedef const char* const_iterator;
    
    		//默认成员函数
    		string(const char* str = "");         //构造函数
    		string(const string& str);              //拷贝构造函数
    		string& operator=(const string& str);   //赋值运算符重载函数
    		~string();                            //析构函数
    
    		//容量操作函数
    		size_t size();
    		size_t capacity();
    		void reserve(size_t n);
    		void resize(size_t n, char ch = '\0');
    		bool empty()const;
    
    		//迭代器相关函数
    		iterator begin();
    		iterator end();
    		const_iterator cbegin();
    		const_iterator cend();
    		const_iterator begin()const;
    		const_iterator end()const;
    
    		//修改字符串相关函数
    		string& operator+=(char ch);
    		string& operator+=(const char* str);
    		string& insert(size_t pos, char ch);
    		string& insert(size_t pos, const char* str);
    		string& erase(size_t pos, size_t len);
    		void push_back(char ch);
    		void append(const char* str);
    		void swap(string& str);
    		const char* c_str()const;
    		void clear();
    
    		//访问字符串相关函数
    		char& operator[](size_t i);
    		const char& operator[](size_t i)const;
    		size_t find(char ch, size_t pos = 0)const;
    		size_t find(const char* str, size_t pos = 0)const;
    		size_t rfind(char ch, size_t pos = npos)const;
    		size_t rfind(const char* str, size_t pos = 0)const;
    
    		//关系运算符重载函数
    		bool operator>(const string& str)const;
    		bool operator<(const string& str)const;
    		bool operator>=(const string& str)const;
    		bool operator<=(const string& str)const;
    		bool operator==(const string& str)const;
    		bool operator!=(const string& str)const;
    
    	private:
    		char* _str;       //存储字符串
    		size_t _size;     //记录字符串当前的有效长度,不包含\0
    		size_t _capacity; //记录字符串当前的容量,不包含\0的大小,但是开辟空间时要包含\0在内
    		static const size_t npos; //静态成员变量(整型最大值)
    	};
    	const size_t string::npos = -1;
    
    	//<<和>>运算符重载函数
    	istream& operator>>(istream& in, string& str);
    	ostream& operator<<(ostream& out, const string& str);
    	istream& getline(istream& in, string& str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    默认成员函数

    构造函数

    构造函数就是开辟空间,给成员变量进行初始化。缺省参数就可以少写一个无参构造函数,若不传参默认生成空字符串。空字符串是有大小的包含了’\0’。

    		string(const char* str = "")
    			:_size(strlen(str))//strlen会检查空指针
    			,_capacity(_size)
    		{
    
    			_str=new char[_capacity + 1];
    			strcpy(_str, str);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    拷贝构造函数

    传统写法:

    		string(const string& str)
    			:_str(new char[strlen(str._str) + 1]) //_str申请一块刚好可以容纳str._str的空间
    			, _size(0)
    			, _capacity(0)
    		{
    			strcpy(_str, str._str);    
    			_size = str._size;        
    			_capacity = str._capacity; 
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    现代写法:

    		//现代写法
    		string(const string& str)
    			:_str(nullptr)
    			,_size(0)
    			,_capacity(0)
    		{
    			string tmp(str._str);//出了作用域对象调用析构
    			swap(tmp);//交换对象
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    赋值运算符重载函数

    传统写法:

    		string& operator=(const string& str)
    		{
    			// 没有处理异常情况,错误情况
    			/*if (this != &str)//防止自己给自己赋值
    			{
    				delete[] _str;
    				_str = nullptr;
    				_str = new char[_capacity + 1];
    				strcpy(_str, str._str);
    				_size=str.size;
    				_capacity=str._capacity;
    
    			}*/
    			if (this != &str)//防止自己给自己赋值
    			{
    				char* tmp = _str = new char[_capacity + 1];
    				strcpy(tmp, str._str);
    				delete[] _str;
    				_str = tmp;
    				_size = str._size;
    				_capacity = str._capacity;
    			}
    
    			return *this;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    注:这里需要先申请空间,如果申请空间失败会抛异常跳到处理异常,不会执行后面的代码,所以先申请空间,发生错误时就不会影响到原有的对象。
    现代写法:
    通过编译器自动调用拷贝构造一个对象str,然后交换对象。

    		//现代写法一、
    	string& operator=( string str)
    		{
    			swap(str);
    			return *this;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    现代写法一、不能判断自己给自己赋值,自己给自己赋值影响也不是很大,就是最后的空间地址发生了变化。

    //现代写法2
    string& operator=(const string& str)
    {
    	if (this != &str) //防止自己给自己赋值
    	{
    		string tmp(str); //用s拷贝构造出对象tmp
    		swap(tmp); //交换这两个对象
    	}
    	return *this; //返回左值(支持连续赋值)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    析构函数

    析构函数需要我们写,因为string类对象都需要开辟动态内存的资源,动态开辟的空间需要我们手动清理。

    		~string()
    		{
    			delete[] _str;
    			_str = nullptr;
    			_size = 0;
    			_capacity = 0;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    迭代器相关函数

    string 的迭代器实际就是char*的别名。

    		typedef char* iterator;
    		typedef const char* const_iterator;
    		// 非const对象调用
    		iterator begin()
    		{
    			return _str;
    		}
    		// 非const对象调用
    		iterator end()
    		{
    			return _str + _size;
    		}
    		// const对象调用
    		const_iterator begin()const
    		{
    			return _str;
    		}
    		// const对象调用
    		const_iterator end()const
    		{
    			return _str + _size;
    		}
    		const_iterator cbegin()
    		{
    			return _str;
    		}
    		const_iterator cend()
    		{
    			return _str + _size;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    注意:end()返回的迭代器执行末尾’\0’。
    范围for的原理:把范围for的代码替换成迭代器访问。

    string str("hello BBQ");
    //编译器将其替换为迭代器形式
    for (auto val : str)
    {
    	cout << val << " ";
    }
    cout << endl;
    
    auto it = s1.begin();
    while (it != s1.end())
    {
    	*it += 1;
    	++it;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    容量和大小相关函数

    size和capacity

    //大小
    size_t size()const
    {
    	return _size; //返回字符串当前的有效长度
    }
    //容量
    size_t capacity()const
    {
    	return _capacity; //返回字符串当前的容量
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    reserve和resize

    void reserve(size_t n)
    {
    	if (n > _capacity)
    	{
    		char* tmp = new char[n+1];
    		strcpy(tmp, _str);
    		delete[] _str;
    
    		_str = tmp;
    
    		_capacity = n;
    	}
    }
    void resize(size_t n, char ch = '\0')
    {
    	if (n > _size)
    	{
    		reserve(n); 
    		memset(_str + _size, ch, n - _size);
    	}
    		_size = n;
    		_str[_size] = '\0';	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    修改字符串相关函数

    insert

    		string& insert(size_t pos, char ch)
    		{
    			assert(pos <= _size);
    			if (_size == _capacity)
    			{
    				reserve(_capacity==0?4:2*_capacity);
    			}
    			size_t end = _size+1;//+1:'\0'也进行处理,后面就不需要末尾添加'\0'了
    			while (pos < end)
    			{
    				_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 (len + _size > _capacity)
    			{
    				reserve(len + _size);
    			}
    			size_t end = _size + len;//+1:'\0'也进行处理,后面就不需要末尾添加'\0'了
    			while (pos+len <= end)
    			{
    				_str[end] = _str[end - len];
    				end--;
    			}
    			strncpy(_str + pos, str, len);//拷贝时不包含'\0'
    			_size += len;
    			return *this;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    push_back

    	void push_back(char ch)
    	{
    			/*if (_size = _capacity)
    			{
    				reserve(2 * _capacity);//增容两倍
    			}
    			_str[_size] = ch;
    			_size++;
    			_str[_size] = '\0';
    			*/
    			insert(_size, ch);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    append

    		void append(const char* str)
    		{
    			/*size_t len = strlen(str);
    			if (len + _size > _capacity)
    			{
    				reserve(len + _size);
    			}
    			strcpy(_str + _size, str);
    			_size += len;*/
    			insert(_size, str);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    operator+=

    		string& operator+=(const string& str)
    		{
    			append(str._str);
    			return *this;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    erase

    		string& erase(size_t pos = 0, 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 -= len;
    			}
    
    			return *this;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    swap

    		void swap( string& str)
    		{
    			std::swap(_size, str._size);
    			std::swap(_capacity, str._capacity);
    			std::swap(_str, str._str);
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    c_str

    		const char* c_str()const
    		{
    			return _str;
    		}
    
    • 1
    • 2
    • 3
    • 4

    访问字符串相关函数

    operator[ ]

    		char& operator[](size_t idx)
    		{
    			assert(idx < _size);
    			return _str[idx];
    		}
    		const char& operator[](size_t idx)const
    		{
    			assert(idx < _size);
    			return _str[idx];
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    find和rfind

    		size_t find(const char* s, size_t pos = 0)
    		{
    			const char* ptr = strstr(_str + pos, s);
    			if (ptr == nullptr)
    			{
    				return npos;
    			}
    			else
    			{
    				return ptr - _str;
    			}
    		}
    		size_t find(char ch)
    		{
    			for (size_t i = 0; i < _size; ++i)
    			{
    				if (ch == _str[i])
    				{
    					return i;
    				}
    			}
    			return npos;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    //反向查找第一个匹配的字符
    size_t rfind(char ch, size_t pos = npos)
    {
    	string tmp(*this); //拷贝构造对象tmp
    	reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
    	if (pos >= _size) //所给pos大于字符串有效长度
    	{
    		pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
    	}
    	pos = _size - 1 - pos; //将pos改为镜像对称后的位置
    	size_t ret = tmp.find(ch, pos); //复用find函数
    	if (ret != npos)
    		return _size - 1 - ret; //找到了,返回ret镜像对称后的位置
    	else
    		return npos; //没找到,返回npos
    }
    
    //反向查找第一个匹配的字符串
    size_t rfind(const char* str, size_t pos = npos)
    {
    	string tmp(*this); //拷贝构造对象tmp
    	reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
    	size_t len = strlen(str); //待查找的字符串的长度
    	char* arr = new char[len + 1]; //开辟arr字符串(用于拷贝str字符串)
    	strcpy(arr, str); //拷贝str给arr
    	size_t left = 0, right = len - 1; //设置左右指针
    	//逆置字符串arr
    	while (left < right)
    	{
    		::swap(arr[left], arr[right]);
    		left++;
    		right--;
    	}
    	if (pos >= _size) //所给pos大于字符串有效长度
    	{
    		pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
    	}
    	pos = _size - 1 - pos; //将pos改为镜像对称后的位置
    	size_t ret = tmp.find(arr, pos); //复用find函数
    	delete[] arr; //销毁arr指向的空间,避免内存泄漏
    	if (ret != npos)
    		return _size - ret - len; //找到了,返回ret镜像对称后再调整的位置
    	else
    		return npos; //没找到,返回npos
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    关系运算符重载函数

    //>运算符重载
    bool operator>(const string& s)const
    {
    	return strcmp(_str, s._str) > 0;
    }
    //==运算符重载
    bool operator==(const string& s)const
    {
    	return strcmp(_str, s._str) == 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    //>=运算符重载
    bool operator>=(const string& s)const
    {
    	return (*this > s) || (*this == s);
    }
    //<运算符重载
    bool operator<(const string& s)const
    {
    	return !(*this >= s);
    }
    //<=运算符重载
    bool operator<=(const string& s)const
    {
    	return !(*this > s);
    }
    //!=运算符重载
    bool operator!=(const string& s)const
    {
    	return !(*this == s);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    >>和<<运算符的重载以及getline函数

    >>运算符的重载

    //>>运算符的重载
    istream& operator>>(istream& in, string& s)
    {
    	s.clear(); //清空字符串
    	char ch = in.get(); //读取一个字符
    	while (ch != ' '&&ch != '\n') //当读取到的字符不是空格或'\n'的时候继续读取
    	{
    		s += ch; //将读取到的字符尾插到字符串后面
    		ch = in.get(); //继续读取字符
    	}
    	return in; //支持连续输入
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    <<运算符的重载

    //<<运算符的重载
    ostream& operator<<(ostream& out, const string& s)
    {
    	//使用范围for遍历字符串并输出
    	for (auto e : s)
    	{
    		cout << e;
    	}
    	
    	//cout<< s.c_str();//这种写法遇到\0结束,不能这么写
    	return out; //支持连续输出
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    getline

    //读取一行含有空格的字符串
    istream& getline(istream& in, string& s)
    {
    	s.clear(); //清空字符串
    	char ch = in.get(); //读取一个字符
    	while (ch != '\n') //当读取到的字符不是'\n'的时候继续读取
    	{
    		s += ch; //将读取到的字符尾插到字符串后面
    		ch = in.get(); //继续读取字符
    	}
    	return in;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    string面试的正确写法
    STL 的string类怎么啦?
    写时拷贝
    写时拷贝的缺陷

  • 相关阅读:
    centos通过nmcli设置静态ip及设置开机自动连接
    线下Meetup:在数智化转型背景下,火山引擎VeDI的大数据技术揭秘
    二分搜索算法框架解析
    shell变量的五种赋值方式
    JDBC封装增删改操作
    传统语音增强——基本谱减法
    少儿编程是智商税吗?不花钱让孩子赢在起跑线
    【送书福利-第二十七期】《边缘计算系统设计与实践》
    Elasticsearch灾备同步方案功能验证(三)
    centos7安装mysql-阿里云服务器
  • 原文地址:https://blog.csdn.net/weixin_58004346/article/details/125878301