<STL系列>string类详解,掌握STL容器从这篇文章开始

c++大大增加了对字符串的支持,除了使用c风格的字符串,还有内置的steing类。string类是c++里STL容器中的一个重要组成部分,在OJ中,和字符串有关的题目通常都以string类的形式出现。而且为了简单方便,在常规工作中都使用string类,很少有人使用c库中的字符串函数。那么接下来,我将带大家来了解一下string类的相关知识。


提示:以下是本篇文章正文内容,下面案例可供参考

一、标准库中的string类

1.string类对象的常见构造

string类有七个构造函数,下面我将挑几个常见的进行演示,如果有小伙伴想对其它进行详细了解,可以进入网址string::string - C++ Reference (cplusplus.com)

代码如下:

int main()
{
	string s1;  //构造空的string类对象
    cout << s1 << endl;
	string s2("hello bit");  //用c字符串构造
    cout << s2 << endl;
	string s3(s2); //拷贝构造
    cout << s3 << endl;
	string s4 = "hello bit";  //这里发生运算符重载,相当于拷贝构造
    cout << s4 << endl;

	string s5(s4, 3, 4);//string (const string& str, size_t pos, size_t len);  
    cout << s5 << endl;                                // 从pos开始,后面的len个字符构造

	
    const char* url = "http://www.cplusplus.com/reference/string/string/string/";
	string s6(url, 4);  //用字符串的前n个字符构造string类
	cout << s6 << endl;

	
    string s7(10, 'x');  //用10个x字符构造
	cout << s7 << endl;

	
    s7 = s2;    //运算符重载
	cout << s7 << endl;


	return 0;
}

打印结果如下:

 2、string类的遍历

2.1.下标+[]

int main()
{
	
	string s2("hello bit");


	for (size_t i = 0; i < s2.size(); ++i)
	{
		// s2.operator[](i)
		s2[i] = 'x';
	}
	cout << endl;

	for (size_t i = 0; i < s2.size(); ++i)
	{
		// s2.operator[](i)
		cout << s2[i] << " ";
		//cout << s2.at(i) << " ";
	}
	cout << endl;

}

size()功能:返回字符串有效字符长度。打印结果如下:

2.2迭代器遍历

我们在这里先简单展示迭代器的使用,在之后的文章中再详细介绍迭代器的原理。

// [begin(), end() ) end()返回的不是最后一个数据位置的迭代器,返回是最后一个位置下一个位置
// 也要注意的是,C++中凡是给迭代器一般都是给的[)左闭右开的区间
// 迭代器是类似指针一样东西,具体是什么我们讲了底层实现才能知道
int main()
{
    string s2("hello bit");
    string::iterator it=s2.begin();
    while(it!=s2.end)
    {
        cout<<*it<<" ";
        ++it;
    }
    
    cout<<endl;

//反向迭代器,反向打印   
    string::reverse_iterator rit = s2.rbegin();
	while (rit != s2.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}

 打印结果:

 2.3范围for遍历

int main()
{
    string s3("123456");
    for(auto& e:s3)
    {
        e+=1;
    }

    for(auto e:s3)
    {
        cout<<e;
    }

    cout<<endl;
    return 0;
}

打印结果如下:

 3、string类的增删查改

 3.1增加

int main()
{
    string s1;
    //尾插字符
	s1.push_back('h');
	s1.push_back('e');
	s1.push_back('l');
	s1.push_back('l');
	s1.push_back('o');
    
    //尾插字符串
	s1.append("world");
	cout << s1 << endl;


    string s2("!!!!");
	s1.append(s2);
	//等价于s1.append(s2.begin(), s2.end());
	cout << s1 << endl;

    //实际最喜欢这样用
    s1 += s2;//等价于s1+="!!!!";
	cout << s1 << endl;

    // 尽量少用insert,因为底层实现是数组,头部或者中间插入需要挪动数据
	s1.insert(1, s2); //等价于s1.insert(1, "!!!!"); 
	cout << s1 << endl;
}

打印结果:

、、

3.2删除

int main()
{
    string s1("hello")
    s1.erase(0, 1);//从0位置开始删除1个字符
	cout << s1 << endl;

	s1.erase(1, 10);//要删除的字符个数大于剩余字符个数不会报错,会把1之后的所有字符删完
	cout << s1 << endl;

	/*s1.erase(3, 10);*/   //这样写会报错
    return 0;
}

3.3查找

find从pos开始向后查找,可以用来查找字符或字符串,默认返回第一个字符的位置。refind是从后向前查找。

 substr用来取出原字符串中的一段字符串。从pos开始,取出后面len长度的字符串。len大于字符串长度会取出后面的所有字符。substr和find结合可以有很多用处,例如取出一个网址的域名。

//用来取出一个网址的域名
string GetDomain(const string& url)
{
	size_t pos = url.find("://");
	if (pos != string::npos)
	{
		size_t start = pos + 3;
		size_t end = url.find('/', start);
		if (end != string::npos)
		{
			return url.substr(start, end - start);
		}
		else
		{
			return string();
		}
	}
	else
	{
		return string();
	}
}

int main()
{
	string s1("hello world");
	cout << s1 << endl;   // 调用 operator<<(cout, s1)
	cout << s1.c_str() << endl; // 调用 operator<<(cout, const char*)

	s1.resize(20);
	
	cout << strlen(s1.c_str()) << endl;//这样写可以算出有效字符的个数
	cout << s1.size() << endl << endl;

    // 要求写一个程序分别取出域名和协议名
	string url1 = "http://www.cplusplus.com/reference/string/string/rfind/";

    cout << GetDomain(url1) << endl;
	
    return 0;
}

3.4 修改 

string的修改比较简单,string类中对[ ]运算符进行了重载,我们可以像数组一样一样直接使用[ ]加下表的方式对string值进行修改。

int main()
{
    string s1="abcde";
    s1[2]='a';
}

4.string类其他常见接口说明

在Capacity部分,主要有如下函数, 下面我将挑几个重要的说明。

4.1size和capacity

看如下代码:

int main()
{
	string  s1("hello world");

	cout << s1 << endl;
	
	cout << s1.size() << endl;  //求s1中字符的个数
	cout << s1.capacity() << endl;   //求s1的容量

	s2.clear();   //清空s1中的字符,但是不改变容量大小
	cout << endl;

	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	return 0;
}

打印结果如下:

 4.2 resize和reserve

resize可以给string开辟特定的空间,并将其中的值初始化,默认的初始化值为/0

int main()
{
	string s1;

	s1.resize(20, 'x'); //如果写成s1.resize(20);默认的初始化值为/0
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	cout << s1 << endl;
}

如果原本string中有值则在后面追加。

int main
{
string s2("hello world");
	s2.resize(20, 'x');
	cout << s2 << endl;
	cout << "size:" << s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl;
	cout << endl;

	s2.resize(5); //输入的值小于原来的值,会保留前5位,删除后面的值
	cout << s2 << endl;
	cout << "size:" << s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl << endl;
}

 

 reserve和revize一样是用来开辟空间的,但区别是reserve不会对开辟的空间进行初始化,如果尾插的话会从第一个位置开始向后添加数据。

二、string模拟实现

学习STL,我们不仅要做到熟练使用它,还要对其中一些重要内容的实现原理有一定了解。相信通过第一部分的介绍,大家对于string类中一些常见接口已经有了一定了解,那么下面就让我们对这些常见接口模拟实现一下吧,这将使我们对string类有更深刻的理解。下面重点介绍如何实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。

注意:如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

1.传统版写法的string类

class string
{
public:
	//构造函数
		string(const char* str = " ")
		{
			if (nullptr == str)
			{
				str = " ";
			}
			
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
	
	//拷贝构造
	string(const string& s)
		: _str(new char[strlen(s._str) + 1])
		,_size(s._size)
		,_capacity(s._capacity)
	{
		strcpy(_str, s._str);
	}
	
	//赋值运算符重载
	string& operator=(const string& s)
	{
		if (this != &s)
		{
			char* pStr = new char[strlen(s._str) + 1];
			strcpy(pStr, s._str);
			delete[] _str;
			_str = pStr;
			_size = s._size;
			_capacity=s._capacity;

		}
		return *this;
	}

	//析构函数
	~string()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}
	}
private:
	char* _str;
	size_t _size;
	size_t _capacity;
}

2.现代版写法的string类

class string
{
public:
	//构造函数
	string(const char* str = " ")
		{
			if (nullptr == str)
			{
				str = " ";
			}
			
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
	
	//交换函数重载
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);

		}
		
		//拷贝构造现代写法
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string temp(s._str);
			swap(temp);
		}

		//赋值运算符现代写法
		string& operator=(string s)
		{
			swap(s);

			return *this;
		}


	//析构函数
	~string()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}
	}
private:
	char* _str;
	size_t _size;
	size_t _capacity;
}

以上主要展示了string类的构造,拷贝,析构,赋值等函数的模拟实现,如果小伙伴们想了解更详细的内容,欢迎进入我的gitee仓库,里面有更加全面的模拟实现string模拟实现 · 15188705658/c++ - 码云 - 开源中国 (gitee.com) 

总结

本文主要介绍了string类的一些常见接口和部分内容的模拟实现,希望大家能通过本文对string类有更加深入的了解,STL的内容还有很多,后面我将继续介绍。如果感觉本篇本章有帮助的话不妨点个赞支持一下博主哦~我也将努力带来更多干货内容,感谢阅读,你的支持就是对我最大的鼓励。

猜你喜欢

转载自blog.csdn.net/weixin_59371851/article/details/124695931
今日推荐