STL--String的使用介绍及其实现

本文介绍了C++中的String类,包括其常用接口、模拟实现和成员介绍。重点讨论了String类的构造、容量操作、遍历及元素修改,并通过代码示例展示了拷贝构造和赋值操作的深拷贝原理,提供了完整的模拟实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

目录

1.什么为String类

2.标准库中的String类

2.1String类的常用接口说明

3.string对象的模拟实现

3.1类成员介绍

4.string模拟实现全代码


1.什么为String

    在c语言中我们在对字符对象进行操作的使用时候常常会注意到字符或者字符串对象都是以'\0'为结尾的,我们想要进行一个字符串的遍历以及增删查改,我们需要创建一个字符数组再来进行操作。这样好像创建一个对象再来进行操作,每次都会消耗我们时间。然而在C++中,之所以说是面向对象的编程,就是我们可以直接可以对STL里面的每个容器进行操作。本章主要介绍String类的使用方法。 

2.标准库中的String类

这是String类的官方介绍,我们通过一些例子会对String有进一步的了解。

2.1String类的常用接口说明

    1.string类对象的常见构造

constructor构造string对象
string()构造一个空的String对象,就是空字符串
String(const char*s)构造函数初始化
string(size_t n,char c)string对象中前n元素都为字符c
string(const string&s)拷贝构造函数
	//构造函数初始化,这里我们用到初始化列表来对
    //构造函数进行初始化,
		string(const char* str="")
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

        int main()
       {

        string s1("hello world!");
		string s2(s1);              //使用s1对象来拷贝一个即将生成的S2对象。
		string s3("sort");

       }

这里在初始化列表给的都是空值,而到了里面的我们需要多给出一个字符的位置来存放’\0‘;

    2.string类对象的容量操作

size返回字符串有效长度哦不包括’\0‘(用法同length两者可以互换)
capacity返回空间总大小
empty检查字符串空间是否为空,空返回true,否则返回false
clear清除有效字符
reserve相当于对字符串空间进行增容。
resize将字符串的有效字符改为n个,多出的字符由给定的值确定
length与size用法相同

     3.string对象的遍历操作

函数名称功能说明
operator【】返回pos位置元素,const string 类对象调用
begin+end获取一个字符的迭代器到end的下一个位置的迭代器
rbegin+rend可以立即为反向迭代器
for(auto e:s1)语法糖,这个称之为语法糖因为用起来很甜。基于迭代器
  •  遍历方式,string遵循的三种遍历方式。
	cout << s1[9] << endl;


	for (auto e : s1)
	{
		cout << e << " ";
	}


	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}

    4.对string对象元素修改操作

函数名称功能说明
push_back尾部插入一个字符
append尾部追加一个字符串
operator+=在原有字符串追加一个字符串str返回被追加的对象
c_str返回c格式字符串
fing+npos从pos位置往后找,返回该字符在字符串中的位置
rfind从pos位置向前找,返回字符位置
substr在str中从pos位置开始,截取n个字符然后将其返回

以上就是string对象的一些常用的函数介绍了~。

3.string对象的模拟实现

3.1类成员介绍

    通过我们以上对string里面函介绍,猜你应该不难想到这么方便一个对象它是怎么实现的呢?其实它的显示就是一个顺序表,因为它还支持随即方访问, 只不过在原有的随机访问上增加了迭代器。

    模拟实现中的成员变量

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

我们可以看到string的实现就还是使用顺序表的方式来整的。不过为一个动态增容的。嗯~~~

我们这里主要介绍一下拷贝构造和赋值的现代比较先进的写法吧,我们先看一下下面这两段拷贝构造的代码分析一下它们有什么不同

	//拷贝构造s3(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)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			Swap(tmp);
		}

以上两端代码都没有问题,但是这里需要注意的是,看似简单的代码其实涉及到了深浅拷贝的问题。浅拷贝问题及是我们在用S1去拷贝构造S2时并没有给S2合适的空间和容量,只是把S1的值拷贝给S2其实这样S2里面的指针就也会指向和S1相同的地址来进行遍历,不过在释放的时候,系统会向将S2所指向的空间释放,然而S1却还指向S2所指向的空间,这样会导致相同的一块空间被重复释放所以我们在初始化列表就对S2进行深拷贝。那么下面这个现代写法深拷贝进去把要被构造的对象全部初始化为空,然后创建一临时对象tmp但是这个临时对象tmp会先去调用它自己的构造函数,然后再构造函数里面使用传过去的s._str来进行初始化,然后回来和this指针进行交换,在交换过后又调用析构函数tmp对象被析构。

    赋值构造

string& operator=(string& s)
		{
							//传统写法赋值
				delete[]_str;
				_str = new char[s._capacity+1];
				_size = s._size;
				_capacity = s._capacity;
				strcpy(_str, s._str);
				//现代写法赋值


			Swap(s);
			return *this;


			//这个是不可以的!!
			delete[]_str;
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			return *this;
		}

这里现代写法也是使用了交换两个对象的内容然后出来调用析构函数。很方便的现代写法

4.string模拟实现全代码

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;
namespace qzh
{
	class string
	{
		friend bool operator<(const string& s1, const string& s2);
	public:
		//迭代器
		typedef char* interator;
		interator begin()
		{
			return _str;
		}
		
		interator end()
		{
			return _str + _size;
		}
			


		//构造函数初始化
		string(const char* str="")
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}



		拷贝构造s3(s1)
		//string(const string& s)
		//	:_size(strlen(s._str))
		//	,_capacity(s._capacity)
		//{
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._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 tmp(s._str);
			Swap(tmp);
		}



		//赋值s3=s1
		string& operator=(string& s)
		{
							//传统写法赋值
				/*delete[]_str;
				_str = new char[s._capacity+1];
				_size = s._size;
				_capacity = s._capacity;
				strcpy(_str, s._str);*/
				//现代写法赋值
			Swap(s);
			return *this;


			//这个是不可以的!!
			/*delete[]_str;
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			return *this;*/
		}


		const char* c_str() const
		{
			return _str;
		}
    
		int size() const
		{
			return _size;
		}

		char& operator[](int pos)
		{
			assert(pos <_size);
			return _str[pos];
		}
		
		
		//扩容
		void resvers(int n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}


		void resize(int n, const char ch = '\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else if(n>_capacity)
			{
				resvers(n);
			}
			memset(_str + _size, ch, n - _size);
			_size = n;
			_str[_size] = '\0';
		}

		int find(const char ch)
		{
			for (int i = 0; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}
			return -1;
		}

		//某个位置插入一个字符或者字符串
		string& insert(int pos,const char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				resvers(_capacity==0?4:_capacity*2);
			}
			size_t end = _size;
			while (pos <end)
			{
				_str[end + 1] = _str[end];
				end--;
			}
			_str[end + 1] = _str[end];
			end--;
			_str[pos] = ch;
			_size++;
			return *this;
		}

		//某个位置插入一个字符串
		string& insert(int pos, const char* s)
		{
			assert(pos <= _size);
			int len = strlen(s);
			if (_size+len>_capacity)
			{
				resvers(_size+len);
			}
			int end = _size+len;
			while (pos+len <= end)
			{
				_str[end] = _str[end - len];
				--end;
			}
			
			strncpy(_str+pos, s, len);
			_size+=len;
			return *this;
			
		}


		/*string& erase(int pos = 0, int 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;
		}*/


		//从尾部插入一个字符
		void pushback(const char ch)
		{
			if (_size == _capacity)
			{
				resvers(_capacity==0?4:_capacity * 2);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}

		//尾部插入一个字符串
		void append(const char* s)
		{
			int len = strlen(s);
			if (_size +len>_capacity)
			{
				resvers(_size + len);
			 }
			strcpy(_str + len, s);
			_size += len;
		}

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


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




	private:
		char* _str;
		int _size;
		int _capacity;
		public:
		//static const size_t npos;
	};

~~~~~~~~~~~~~

03-24
### C++ STL Map 的基本用法 `std::map` 是 C++ 标准模板库 (STL) 提供的一种关联容器,它基于红黑树实现[^3]。这种数据结构允许存储键值对,并自动按照键的顺序排列。以下是 `std::map` 的一些核心功能及其使用方法: #### 创建和初始化 可以创建一个空的 `std::map` 或者通过列表初始化器来填充初始值。 ```cpp #include <iostream> #include <map> int main() { std::map<int, std::string> myMap; // 创建一个空 map // 使用列表初始化器 std::map<char, int> anotherMap = { {'a', 1}, {'b', 2} }; return 0; } ``` #### 插入元素 可以通过多种方式向 `std::map` 中插入新元素。 ```cpp myMap[1] = "one"; // 方括号操作符会插入或更新已存在的键 myMap.insert({2, "two"}); // insert 方法显式插入一对 key-value myMap.emplace(3, "three"); // emplace 更高效地构建并插入对象 ``` #### 查找元素 查找特定键是否存在以及获取对应的值是一个常见需求。可以利用成员函数 `find()` 来完成此任务[^2]。 ```cpp auto it = myMap.find(2); if(it != myMap.end()) { std::cout << "Key found: " << it->first << ", Value: " << it->second << '\n'; } else { std::cout << "Key not found\n"; } ``` #### 删除元素 删除指定键的条目或者清除整个映射都可以轻松做到。 ```cpp myMap.erase(1); // 移除键为 1 的元素 myMap.clear(); // 清空所有元素 ``` #### 遍历 遍历所有的键值对通常采用迭代器来进行。 ```cpp for(const auto& pair : myMap){ std::cout << pair.first << ": " << pair.second << "\n"; } ``` #### 自定义比较函数 为了更灵活地控制如何排序键,可以提供自定义的比较逻辑给 `std::map` 构造函数[^1]。 ```cpp struct GreaterThanComparator{ bool operator()(const int lhs, const int rhs)const{ return lhs > rhs;} }; std::map<int,std::string,GreaterThanComparator> descendingMap; descendingMap[5]="five"; descendingMap[1]="one"; // 输出将按降序显示因为用了自定义比较器 for(auto &p : descendingMap){ std::cout<< p.first <<": "<< p.second<<"\n"; } ``` ### 性能考量 由于内部采用了平衡二叉搜索树的数据结构,因此大多数涉及单个元素的操作时间复杂度均为 O(log n)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值