C++STL第一篇:string类介绍及模拟实现

Posted 无聊的马岭头

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++STL第一篇:string类介绍及模拟实现相关的知识,希望对你有一定的参考价值。

前言

一、标准库中的string类

1.1 string类

  1. 字符串的表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
    单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
    息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
    和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个
    类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

2.2 string类中的常用接口说明+模拟实现

2.2.1 string类对象的常见构造+模拟实现
函数名称功能说明
string();构造一个空字符串
string (const string& str);拷贝构造函数
string (const string& str, size_t pos, size_t len = npos);复制从字符位置开始的str部分,并跨度len字符
string (const char* s);用字符串构造string类对象
string (const char* s, size_t n);构造s所指向的字符序列,赋值n个字符
string (size_t n, char c);构造一个字符序列为n个c的类对象

代码演示

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1;
	string s4("hello world");
	string s5("hello world", 7);
	string s6(10, 'x');
	string s2(s4);
	string s3(s4, 6, 3);

	cout << "s1:"<< s1.c_str() << endl;
	cout << "s4:" << s4.c_str() << endl;
	cout << "s5:" << s5.c_str() << endl;
	cout << "s6:" << s6.c_str() << endl;
	cout << "s2:" << s2.c_str() << endl;
	cout << "s3:" << s3.c_str() << endl;	
}

运行结果

模拟实现
由于上面有些接口不常用,所以我就模拟实现了一部分常用的接口

string (const char* s)
namespace cxy
{
	class string
	{
	public:
		string(const char*s = "")
		{
			if (s==nullptr)
				return;
			_size = strlen(s);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, s);
		}
		
		const char* c_str()
		{
			return _str;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
string (const string& str)
void swap (string& str)
namespace cxy
{
	class string
	{
	public:
		void swap(string& str)
		{
		//下面的swap会调用库里面的接口
			::swap(_size, str._size);
			::swap(_capacity, str._capacity);
			::swap(_str, str._str);
		}

		string(const char*s = "")
		{
			if (s==nullptr)
				return;
			_size = strlen(s);
			_capacity = _size;
			_str = new char[_capacity + 1];

			strcpy(_str, s);
		}

		string(const string& str)
			:_str(nullptr), _size(0), _capacity(0)
		{
			string tmp(str._str);
			swap(tmp);
		}

		char* c_str()
		{
			return _str;
		}

	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
2.2.2 string类对象的容量操作+模拟实现
函数功能说明
size_t size() const; (重点)返回字符串有效长度
size_t length() const;返回字符串有效长度
size_t capacity() const;返回空间的大小
bool empty() const; (重点)检测字符串释放为空串,是返回true,否则返回false
void clear();(重点)清空有效字符
void reserve (size_t n = 0);(重点)请求改变容量,为字符串预留空间
void resize (size_t n, char c);(重点)将有效字符的个数该成n个,多出的空间用字符c填充

代码演示

int main()
{
	string s1("hello world");
	cout <<"s1.size(): " <<s1.size() << endl;
	cout <<"s1.length(): "<< s1.length() << endl;
	cout <<"s1.capacity(): "<<s1.capacity() << endl;
	cout <<"s1:"<< s1 << endl;
	cout << endl;

	s1.clear();
	cout <<"s1:"<< s1 << endl;
	cout << "s1.size(): " << s1.size() << endl;
	cout << "s1.capacity(): " << s1.capacity() << endl;
	cout << endl;

	s1 = "hello world";
	cout << "s1:" << s1 << endl;
	cout << "s1.size(): " << s1.size() << endl;
	cout << "s1.capacity(): " << s1.capacity() << endl;
	s1.resize(17,'x');
	//当n>capacity,则扩容,并且把0~27上位置的空余位置填充‘字符’
	cout << "s1:" << s1 << endl;
	cout << "s1.size(): " << s1.size() << endl;
	cout << "s1.capacity(): " << s1.capacity() << endl;
	s1.resize(27, 'x');
	//当size<n<capacity,则把0~27上位置的空余位置填充‘字符’
	cout << "s1:" << s1 << endl;
	cout << "s1.size(): " << s1.size() << endl;
	cout << "s1.capacity(): " << s1.capacity() << endl;
	s1.resize(5, 'x');
	//当n<size,则只保留n个‘字符’,空间大小不变
	cout << "s1:" << s1 << endl;
	cout << "s1.size(): " << s1.size() << endl;
	cout << "s1.capacity(): " << s1.capacity() << endl;
	cout << endl;

	string s2("hello world");
	s2.reserve(5);
	//当n<=capacity时,空间大小不变,且不改变数据内容
	cout << "s2:" << s2 << endl;
	cout << "s2.size(): " << s2.size() << endl;
	cout << "s2.capacity(): " << s2.capacity() << endl;

	s2.reserve(100);
	//当n>capacity时,空间会增大
	cout << "s2:" << s2 << endl;
	cout << "s2.size(): " << s2.size() << endl;
	cout << "s2.capacity(): " << s2.capacity() << endl;
}

运行结果

得知
reserve和resize的区别:reserve不会影响内容,resize会影响内容。

模拟实现

size_t size() const
返回字符串的有效长度
namespace cxy
{
	class string
	{
	public:
		size_t size()const
		{
			return _size;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
size_t capacity() const
返回空间的大小
namespace cxy
{
	class string
	{
	public:
		size_t capacity()const
		{
			return _capacity;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
bool empty() const
检测字符串释放为空串,是返回true,否则返回false
namespace cxy
{
	class string
	{
	public:
		bool empty()const
		{
			return _str == 0;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
void clear()
清空有效字符 ,不会改变容量
namespace cxy
{
	class string
	{
	public:
		void clear()
		{
			_size = 0;
			_str[_size] = '\\0';
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
void reserve (size_t n = 0)
请求改变容量 ,此功能对字符串长度没有影响,无法改变其内容
  • 如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 字符(或更大)
  • n小于当前字符串容量时,不会发生改变
namespace cxy
{
	class string
	{
	public:
		void reserve(size_t n=0)
		{
			if (n > _capacity)
			{
				char *tmp = new char[n + 1];
				strncpy(tmp,_str,_size+1);
				delete[]_str;

				_str = tmp;
				_capacity = n;
			}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}

补充:strncpy是C语言中的函数

char * strncpy ( char * destination, const char * source, size_t num )

功能

  1. 将source中的字符串复制到destination中,且复制num个字符个数,如果在没有复制完num个字符之前,找到了source的末尾,则目标填充零,直到向其编写了总共num字符。
  2. 如果来source中的字符有效长度大于数字,则目的地末尾不会隐含任何空字符(’\\0’)。
  3. 因此,在这种情况下,目的地不应被视为无效终止的 C 字符串(因此读取它将溢出,所以这种时候记得要在末尾添加’\\0’)。
void resize (size_t n, char c)
void resize (size_t n)
将有效字符的个数该成n个,多出的空间用字符c填充
  • 将字符串大小重新变为n字符的长度。
  • 如果 n 小于当前字符串长度,则当前值将缩短为其第一个 n 字符,从而删除 n 之外的字符。
  • 如果 n 大于当前字符串长度,则通过在末尾插入尽可能多的字符c以达到 n 的大小来扩展当前内容。
  • 如果指定c,则新元素初始化为c的副本,否则,它们是值初始化字符(空字符)。
namespace cxy
{
	class string
	{
	public:
		void resize(size_t n,char c='\\0')
		{
			if (n<_size)
			{
				_size = n;
				_str[_size] = '\\0';
			}
			else
			{		
				if (n > _capacity)
				{
					reserve(n);
				}
				memset(_str + _size, c, n - _size);			
				_size = n;
				_str[_size] = '\\0';
			}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}

补充:memset是C语言中的函数

void * memset ( void * ptr, int value, size_t num )

功能

  • 将value传到prt中,以第一个位置开始传,传num个,传完为止。

【大总结】

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
  2. clear()只是将string中有效字符清空,不改变底层空间大小。
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
2.2.3 string类对象的访问及遍历操作+模拟实现
函数功能说明
char& operator[] (size_t pos); 和 const char& operator[] (size_t pos) const;返回pos位置的字符,const string类对象调用
iterator begin();获取一个字符的迭代器
iterator end();获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式

代码演示

int main()
{
	string s("hello world");
	cout << "operator[] :";
	for (size_t i = 0; i < s.size(); i++)
		cout << s[i] ;
	cout << endl;
	//迭代器
	string::iterator it = s.begin();
	cout << "iterator :";
	while (it != s.end())
	{
		cout << *it ;
		++it;
	}
	cout << endl;
	//范围for
	cout << "范围for :";
	for (auto ch : s)
	{
		cout << ch ;
	}
	cout << endl;
}


模拟实现

const char& operator[] (size_t pos) const
namespace cxy
{
	class string
	{
	public:
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
iterator begin()iterator end()
namespace cxy
{
	class string
	{
	public:
		typedef char* iterator;		
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str+_size;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
C++11:范围for

在这里不实现,知道怎么用就行

2.2.4 string类对象的修改操作+模拟实现
函数功能说明
void push_back (char c);在字符串后面插入字符c
string& append (const char*s);在字符串后面追加字符串s
string& operator+= (const char* s);在字符串后面追加字符串s
const char* c_str() const;返回c格式的字符串
size_t find (const char* s, size_t pos = 0) const;从字符串pos位置开始往后找字符串s,返回该字符串s在字符串中的位置
string& erase (size_t pos = 0, size_t len = npos);擦除字符串的一部分,减少其长度

代码演示

int main()
{
	string s("hello world");
	s.push_back('K');
	cout << s << endl;
	s.append("SSSSS");
	cout << s << endl;
	s += "FF";
	cout << s << endl;
	cout << s.find("KSS") << endl;
	s.erase(11, 8);
	cout << s << endl;
}

运行结果
模拟实现:只实现了一些常用的接口

void push_back (char c)
在字符串后面插入字符c
namespace cxy
{
	class string
	{
	public:
		void push_back(char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity * 2);
			}
			_str[_size] = c;
			_str[_size+1] = '\\0';
			_size++;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
string& append (const char*s)
在字符串后面追加字符串s
namespace cxy
{
	class string
	{
	public:
		string &append(const char*s)
		{
			size_t len = strlen(s)+_size;
			if (len > _capacity)
			{
				reserve(len);
			}
			strncpy(_str + _size, s, len - _size+1);
			_size = len;
			return *this;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
string& operator+= (const char* s)
在字符串后面追加字符串s
namespace cxy
{
	class string
	{
	public:
		string& operator+=(const char*s)
		{
			append(s);
			return *this;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	};
}
const char* c_str() const
返回c格式的字符串
namespace cxy
{
	class string
	{
	C++STL之string类的使用和实现

C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现

C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现

C++STL第二篇:vector类的介绍及模拟实现

C++STL第二篇:vector类的介绍及模拟实现

[C/C++]详解STL容器7--红黑树的介绍及部分模拟实现