当前位置: 首页 > news >正文

string类--C++

目录

一、std::string类的基本使用(常用)

1.1、初始化与赋值

1.2、字符串的拼接

1.3、string类对象的访问及遍历操作

1.4、长度与容量操作

1.5、子字符串与查找

1.6、string类非成员函数

二、模拟实现string类核心功能

2.1、类定义与成员变量

2.2、构造与析构函数

2.3、深拷贝

2.4、扩容机制

三、完整代码


为什么需要std::string类?

在C语言中字符串是通过字符数组来管理的,虽然C标准库提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不符合面向对象编程的思想。并且需要手动内存管理(容易内存泄漏或越界),缺乏便捷的操作(查找之类的),无法动态调整大小。

string类的优势:

而在C++中,string作为标准库容器,能自动内存管理,提供了很多接口函数,还集成了STL的算法(algorithm)库。使用起来更便捷、安全。

一、std::string类的基本使用(常用)

1.1、初始化与赋值

1.2、字符串的拼接

               函数名称                                                功能
        push_back在字符串后尾插个字符C
        append在字符串后追加一个字符串
        oprator+=在字符串后追加字符串

比如:

1.3、string类对象的访问及遍历操作

可以通过多种方式访问和修改字符

        函数名                                                功能
       operator[]

返回pos位置的字符

            at()返回pos位置的字符
 begin() 、end()begin获取一个字符的迭代器 、 end获取最后一个字符下一个位置的迭代器
rbegin()、rend()begin获取一个字符的迭代器 、 end获取最后一个字符下一个位置的迭代器

这里operator[]与at()区别在于at会进行边界检查,如果超过边界,编译器会抛异常。前者不检查边界

1.4、长度与容量操作

        函数名                                                        功能
        size返回字符串有效字符长度
        length返回字符串有效字符长度
       capacity        返回空间总大小
        empty检查字符串是否为空串,是返回true,否则返回false
        clear清空有效字符
        reserve为字符串预留空间(扩容)
       resize将有效字符改为n个,多出的空间用字符c填充

注意:

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不会改变容量大小。

1.5、子字符串与查找

        函数名                                                功能
        find从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
        rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
 find_first_of在字符串中查找字符(从前往后找)
find_last_of在字符串中查找字符(从后往前找)
        substr在str中从pos位置开始,截取n个字符,然后将其返回
       c_str将 C++ 字符串转换为 C 风格字符串(即以空字符 \0 结尾的字符数组)

const char* c_str() const,

返回指向字符串内部字符数组的 const char* 指针

#include <string>
#include <stdio.h>

int main() 
{
    std::string s = "Hello World";
    const char* cstr = s.c_str();
    printf("%s\n", cstr); // 输出 "Hello World"
    return 0;
}

1.6、string类非成员函数

        函数名                                                        功能
     operator+尽量少用,因为传值返回,导致深拷贝效率低
    operator>> 输入运算符重载
    operator<<输出运算符重载
    getline获取一行字符串
relational operators大小比较

operator>>无法读取包含空格的整行文本(遇到空格停止)。

std::string s;
std::cin >> s;  // 输入 "Hello World" 时,s = "Hello"

getline:

从输入流中读取整行文本(包括空格),直到遇到换行符或指定分隔符。

std::string s;
std::getline(std::cin, s);         // 默认以换行符分隔
std::getline(std::cin, s, ';');     // 读取直到遇到分号

relational operators(重载比较运算符):

比较两个字符串的字典序,支持 ==!=<><=>=

std::string a = "apple";
std::string b = "banana";
if (a < b) {  // true,"apple" 字典序小于 "banana"
    // ...
}

按 ASCII 码值比较。


二、模拟实现string类核心功能

2.1、类定义与成员变量

class string 
{
private:
    size_t _capacity;  // 当前分配的内存容量
    size_t _size;      // 字符串实际长度(不含'\0')
    char* _str;        // 动态分配的字符数组
public:
    const static size_t npos = -1;  // 表示无效位置的常量
};

2.2、构造与析构函数

构造函数:

使用memcpy而非strcpy,避免字符串中间存在 \0 导致截断。

参数 “ ” 是确保空字符串初始化为 \0.

析构函数:


2.3、深拷贝

拷贝构造:

赋值运算符重载:

避免if (this != &s)导致的资源错误释放。

2.4、扩容机制


三、完整代码

#pragma once

#include<assert.h>
#include<iostream>
using namespace std;

namespace lsg
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}

		/*string()
			:_capacity(0)
			, _size(0)
			, _str(new char[1])
		{
			_str[0] = '\0';
		}*/

		string(const char* s = "")//空字符串是有一个\0的
			:_capacity(strlen(s))
			,_size(_capacity)
			,_str(new char[_capacity+1])
		{
			//strcpy(_str, s);
			memcpy(_str, s, _size + 1);
		}

		//拷贝构造
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			//strcpy(_str, s._str);这个不行,要是string对象中间有'\0','\0'后面的也是应该要拷贝的
			memcpy(_str, s._str, s._size + 1);
			_size = s._size;
			_capacity = s._capacity;
		}

		//实现的是深拷贝
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				memcpy(_str, s._str, s._size + 1);
				delete[] _str;
				_str = tmp;
				
				_size = s._size;
				_capacity = s._capacity;
			}

			return *this;
		}

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

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

		size_t size() const
		{
			return _size;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);

			return _str[pos];
		}

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

			return _str[pos];
		}

		void reserve(size_t len)
		{
			//如果传过来的len小于_capacity不缩括
			if (len > _capacity)
			{
				char* tmp = new char[len + 1];
				//strcpy(tmp, _str);这个也一样
				memcpy(tmp, _str, _size + 1);
				delete[] _str;
				_str = tmp;
				_capacity = len;
			}
		}

		void resize(size_t n, char ch = '\0')
		{
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				reserve(n);

				for (size_t i = _size;i < n;i++)
				{
					_str[i] = ch;
				}

				_size = n;
				_str[_size] = '\0';
			}
		}

		void push_back(const char ch)
		{
			if (_size == _capacity)
			{
				//2倍扩容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}
		
		void append(const char* str)
		{
			size_t len = strlen(str);

			if (_size + len > _capacity)
			{
				//至少扩到—_size+len
				reserve(_size + len);
			}

			//strcpy(_str + _size, str);//可换可不换
			memcpy(_str + _size, str, len + 1);
			_size += len;
		}

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

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

		void insert(size_t pos, size_t n, const char ch)
		{
			assert(pos <= _size);

			//扩容
			if (_size + n > _capacity)
			{
				reserve(_size + n);
			}

			//挪数据
			int end = _size;
			//while (end >= (int)pos)//当pos==0时要强转,整型提升
			while (end >= pos && end != npos)//end==-1等于npos
			{
				_str[end + n] = _str[end];
				end--;
			}

			//插数据
			for (int i = 0;i < n;i++)
			{
				_str[pos + i] = ch;
			}
			_size += n;
		}

		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			//挪数据
			int end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			//插入数据
			for (int i = 0;i < len;i++)
			{
				_str[pos + i] = str[i];
			}

			_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
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}

		size_t find(char ch, size_t n = 0)
		{
			assert(n <= _size);

			for (size_t i = n;i < _size;i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* str, size_t n = 0)
		{
			assert(n <= _size);

			const char* ptr = strstr(_str, str);

			if (ptr)
			{
				return ptr - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= _size);

			size_t n = len;
			if (len == npos || pos + len > _size)
			{
				n = _size - pos;
			}

			string tmp;
			tmp.reserve(n);
			for (size_t i = pos;i < pos + n;i++)
			{
				tmp += _str[i];
			}

			return tmp;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

		bool operator<(const string& s) const 
		{
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);

			//"hello" "hello"  false
			//"helloxx" "hello" false
			//"hello" "helloxx"  true
			return ret == 0 ? _size < s._size : ret < 0;
		}

		bool operator==(const string& s) const
		{
			return _size == s._size && memcmp(_str, s._str, _size) == 0;
		}

		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);
		}

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

	public:
		const static size_t npos;

	};
	const size_t string::npos = -1;
}

ostream& operator<<(ostream& out, const lsg::string& s)
{
	/*for (size_t i = 0;i < s.size();i++)
	{
		out << s[i];
	}*/

	for (auto ch : s)
	{
		out << ch;
	}

	return out;
}

istream& operator>>(istream& in, lsg::string& s)
{
	//s.clear();//每次流提取前要覆盖掉之前的

	//char ch = in.get();
	in >> ch;//这样读不到空格和换行
	//while (ch != ' ' && ch != '\n')
	//{
	//	s += ch;
	//	ch = in.get();
	//}

	//优化######################################
	s.clear();//每次流提取前要覆盖掉之前的

	char ch = in.get();
	//处理前缓冲区的空格和换行
	while (ch == ' ' || ch == '\n')
	{
		ch = in.get();
	}

	char buff[128];
	int i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}

	if (i != 0)
	{
		buff[i] = '\0';
		s += buff;
	}

	return in;
}

相关文章:

  • 场景题:如何设计一个抢红包随机算法
  • 解析漏洞总结
  • Java 24新特性概述
  • 【初学者】Python语言中有没有指针类型?
  • 夯实 kafka 系列|第一章:初识 kafka
  • 模型(分类模型、回归模型、聚类模型)的评分指标
  • dns实现主服务器
  • leetcode hot100(五)
  • 【实用部署教程】olmOCR智能PDF文本提取系统:从安装到可视化界面实现
  • 企业年度经营计划制定与管理方法论(124页PPT)(文末有下载方式)
  • CSS Grid 布局
  • JVM OOM问题如何排查和解决
  • 面试提问:如何判断 Hive 表是内部表还是外部表?
  • MySQL 入门大全:运算符
  • 基于Selenium Grid的分布式测试架构设计与深度实践
  • springboot444-基于Vue的网络小说交流平台(源码+数据库+纯前后端分离+部署讲解等)
  • 【初学者】请介绍一下线性与非线性的区别?
  • Unity—从入门到精通(第一天)
  • 详细解析格式化消息框的代码
  • defineAsyncComponent和一般的import有什么区别
  • 贝壳一季度收入增长42%:二手房市场活跃度维持在高位
  • 车建兴被留置:跌落的前常州首富和红星系重整迷路
  • 特朗普中东行:“能源换科技”背后的权力博弈|907编辑部
  • 机构发布“2025中国高职院校排名”
  • 香港特区立法会通过条例草案便利外地公司迁册来港
  • 4月新增社融1.16万亿,还原地方债务置换影响后信贷增速超过8%