首页 > 编程语言 >C++STL string简单实现

C++STL string简单实现

时间:2024-08-18 19:24:02浏览次数:13  
标签:const string STL pos C++ char str size

文章目录


前言

    std::string是C++标准模板库中的一个类,用于处理字符串。与C语言的字符串相比,std::string 提供了更强大的功能和更安全的操作, 这次我们来简单实现一下

string接口介绍

构造函数

    std::string提供了多种构造函数,允许灵活地创建字符串对象, 下面举例几个最常用的

std::string();//默认构造函数, 创建一个空的字符串
std::string(const char* s);//使用C语言字符串创建
std::string(const std::string& str);//拷贝构造
std::string(const char* s, size_t n);//使用C语言字符串构造, 指定长度
std::string(size_t n, char c);//使用n个字符c构造
template <class InputIterator>
std::string(InputIterator first, InputIterator last);//迭代器构造
std::string(const std::string& str, size_t pos, size_t len = npos);//使用另一个string的部分构造

迭代器

    std::string也提供了迭代器来遍历和操作其内容

iterator begin();//返回字符串开头的迭代器
const_iterator begin() const;//常量迭代器
iterator end();//返回字符串结尾的迭代器
const_iterator end() const;
reverse_iterator rbegin();//返回字符串开头的反向迭代器
const_reverse_iterator rbegin() const;
reverse_iterator rend();//返回字符串结尾的反向迭代器
const_reverse_iterator rend() const;

    std::string::iterator 是string里封装的一个类型, 类似于指针
使用示例

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

    上面是一个使用迭代器遍历的例子, STL的容器几乎都能使用迭代器遍历
it++移动迭代器, *it获取当前迭代器的引用, 使用起来和指针差不多, 当然如果绝对这个类型太长, 也可以

auto it=str.begin();

反向迭代器使用一样, 不够是从字符串的末尾开始遍历。
至于const迭代器, 不能通过*it等方式修改迭代器指向的值

常用容量操作

size_t size() const;//获取字符串的字符个数
size_t length() const;//同上
void resize (size_t n);//开n个字符的空间
void resize (size_t n, char c);//开n个字符的空间, 并都初始化为 c
void clear();//清空字符串
bool empty() const;//判断字符串是否为空

    size和length没有区别, 都是获取字符串的字符个数, resize一般用于提前开空间, string的数据是一个动态管理内存开辟的, 会自动扩容(扩容是有代价的), 提前开取足够的空间可以减少扩容的次数, clear可以清空字符串为空串

元素访问操作

char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;

    string实现了[]重载, 使得可以像使用数组一样访问该下标的元素

char& at (size_t pos);
const char& at (size_t pos) const;

    取pos位置的元素

char& back();
const char& back() const;
char& front();
const char& front() const;

    访问第一个位置的元素和最后一个位置的元素

常用操作

string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
string& operator+= (initializer_list<char> il);

    string也实现了+=操作符重载, 使得拼接字符串更方便

void push_back (char c);
void pop_back();

    尾插尾删操作, 经典

string& insert (size_t pos, const string& str);
...
iterator insert (const_iterator p, char c);
...
string& erase (size_t pos = 0, size_t len = npos);
iterator erase (const_iterator p);
iterator erase (const_iterator first, const_iterator last);

    insert/erase一系列操作, 指定位置插入/删除或者指定迭代器位置插入/删除

void swap (string& str);

    用于交换两个字符串的资源

static const size_t npos = -1;
size_t find (const string& str, size_t pos = 0) const noexcept;
size_t find (const char* s, size_t pos = 0) const;
size_t find (const char* s, size_t pos, size_type n) const;
size_t find (char c, size_t pos = 0) const noexcept;
size_t rfind (const string& str, size_t pos = npos) const noexcept;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos, size_t n) const;
size_t rfind (char c, size_t pos = npos) const noexcept;

    find系列函数, 返回找到了第一个匹配的下标, 找不到返回npos

string substr (size_t pos = 0, size_t len = npos) const;

    提取子字符串

char* c_str();
const char* c_str() const;

string简单实现

    我们已经认识了string的基本接口使用, 现在我们自己简单模拟实现一下吧
完整代码(Github)

框架

    只实现了常用的接口, 这里只介绍一些重点方法实现, 其他的可以去我的GitHub仓库看源码

class string
{
public:
    //迭代器
	typedef char* iterator;
	typedef const char* const_iterator;
	iterator begin();
	iterator end();
	const_iterator begin()const
	const_iterator end()const
    //构造析构函数
	string(const char* str = "");
	string(const string& str);
	~string();
    //空间
	size_t size()const;
	void reserve(size_t capacity);//这个用来扩容
	void resize(size_t size);
	size_t capacity()const;
	void swap(string& str);
	void clear();
    //插入删除
	void push_back(char c);
	string& append(const string str);
	string& insert(size_t pos, const string& str);
	string& insert(size_t pos, char c);
	string& erase(size_t pos, size_t n = npos);
	string& repalce(size_t pos, size_t n, const string& str);
    //获得C风格的字符串
	char* c_str();
	const char* c_str() const;
    //查找
	size_t find(const string& str, size_t pos=0);
    //提取子字符串
	string substr(size_t pos, size_t len = string::npos);
    //运算符重载
	char& operator[](size_t pos);
	const char& operator[](size_t pos) const;
	string& operator+= (const string& str);
	string& operator+= (char c);
	string operator+(const string& str) const;
	string& operator= (string str);
	static size_t npos;
private:
	char* _str;//字符数组
	size_t _size;//字符个数
	size_t _capacity;//容量
};
size_t string::npos = -1;
std::ostream& operator<<(std::ostream& out, M::string& str);
std::istream& operator>>(std::istream& in, M::string& str);

构造/析构函数

string(const char* str = "")
{
	_capacity = strlen(str);
	_str = new char[_capacity + 1] {0};
	_size = _capacity;
	strcpy(_str, str);
}
string(const string& str)
{
	string s(str.c_str());
	swap(s);
}
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = 0;
	_capacity = 0;
}

注意

  • 这里设定capacity和sized都为不包含’\0’的大小, 所有要多开一个空间给’\0’
  • 拷贝构造里面的s是一个临时变量, 函数栈帧销毁后自动释放, 所有我们可以直接交换和它的资源, 既能拿取它的资源, 还能让自己之前的资源释放
  • 构造使用了new[],析构也要相应匹配

迭代器

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;
}
  • 这里直接使用char* 可以完成迭代器的功能

操作符重载

[]+=重载

char& operator[](size_t pos)
{
	assert(pos <= _size);
	return _str[pos];
}
string& operator+= (const string& str)
{
	if (_size + str.size() > _capacity)
	{
		reserve(_size + str.size());
	}
	strcpy(_str + _size, str.c_str());
	_size += str.size();
	return *this;
}
string& operator+= (char c)
{
	push_back(c);
	return *this;
}
string operator+(const string& str) const
{
	string s(*this);
	s += str;
	return s;
}
string& operator= (string str)
{
	swap(str);
	return *this;
}

注意

  • +=操作符重载要支持连续+=
  • +操作符的返回值是两个字符串拼接的结果, 但原本的字符串不能改变
  • =操作符要支持连接=

流插入/提取操作符重载

std::ostream& operator<<(std::ostream& out, M::string& str)
{
	for (int i = 0; i < str.size(); i++)
	{
		out << str.c_str()[i];
	}
	return out;
}
std::istream& operator>>(std::istream& in, M::string& str)
{
	char ch=in.get();
	char strbuff[128];
	int i = 0;
	while (ch != ' ' && ch != '\n')
	{
		strbuff[i++]=ch;
		if (i == 127)
		{
			strbuff[i] = '\0';
			str += strbuff;
			i = 0;
		}
		ch = in.get();
	}
	if (i != 0)
	{
		strbuff[i] = '\0';
		str += strbuff;
	}
	return in;
}

注意

  • 流插入/提取操作符第一个参数必须是i/ostream对象, 这些函数不能写在类里面, 如果把这些函数写在类里面, 第一个参数会被this指针占用
  • 流提取使用了缓冲区, 避免多次调用+=

常用操作

插入

void push_back(char c)
{
	if (_size == _capacity)
	{
		size_t new_capacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(new_capacity);
	}
	_str[_size] = c;
	_size++;
}
string& insert(size_t pos, const string& str)
{
	if (_size + str.size() > _capacity)
	{
		reserve(_size + str.size());
	}
	size_t end = str.size()+_size;
	while (end > pos)
	{
		_str[end] = _str[end - str.size()];
		--end;
	}
	strncpy(_str + pos, str.c_str(),str.size());
	_size += str.size();
	return *this;
}
string& insert(size_t pos, char c)
{
	if (_size + 1 > _capacity)
	{
		reserve(_size + 1);
	}
	size_t end = 1 + _size;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = c;
	_size ++;
	return *this;
}
string& append(const string str)
{
	(*this)+= str;//直接偷懒复用+=
	return *this;
}

注意

  • 满了需要扩容, 我这里选择的第一次开辟4个空间, 接下面都是2倍扩容
  • insert也要提前计算空间大小, 然后移动位置插入

删除

string& erase(size_t pos, size_t n = npos)
{
	if (n == npos || n + pos >= _size)
	{
		_str[pos] = '\0';
	}
	else
	{
		strcpy(_str + pos, _str + pos + n);
	}
	return *this;
}

注意

  • 删除的数量大于数据数直接将后面全部清空, 删除不缩容

标签:const,string,STL,pos,C++,char,str,size
From: https://blog.csdn.net/2301_77838258/article/details/141303998

相关文章

  • String和StringBuilder的区别
    //创建一个控制台应用程序,在Mian()方法中编写如下代码,验证字符串操作和可变字符串操作的执行效率。stringstr="";longlongStartTime=DateTime.Now.Millisecond;for(inti=0;i<10000;i++){......
  • c++--基础语法
    frompixiv参考博客ChatgptC++基础-知识点修饰符const在C++中,const关键字用于定义不可修改的变量、指针、函数参数和返回值等。它可以增强代码的安全性和可读性,防止意外修改数据。1.常量变量使用const定义的变量是不可更改的常量。一旦赋值,就不能再修改。cons......
  • StringBuilder类相关操作
     //StringBuilder的定义及相关操作intint1=100;StringBuilderstr1=newStringBuilder("哈哈哈,",100);str1.Append("你变了");//Append函数Console.WriteLine(str1);str1.Appe......
  • Qt/C++地图标注点的添加删除移动旋转/指定不同图标和动图/拿到单击信号
    一、前言说明标注点在地图开发中是最常见的应用场景之一,比如在地图上需要显示设备的位置,基本上都是添加标注点,指定图片和尺寸已经经纬度坐标位置。这个功能在每种地图内核中都提供的,这个并没有任何难点,在这个功能点上最大难题或者说是设计细节就是,标注点该如何对齐,比如水滴形状的......
  • 不可变字符串string的相关操作
    staticvoidMain(string[]args){//截取字符串stringstr1="ABCDEFGHIJKLMN";stringstr2=str1.Substring(0,4);//从0位开始截取,共截取4位;Console.WriteLine(str2);Console.WriteLin......
  • C++学习第二课
    一、C++数据类型使用编程语言进行编程时,我们需要用到各种变量来存储各种信息,变量保留的时他所存储的值的内存位置,这意味着,你创建一个变量时,就会在内存中保留一些空间。在我们的编程中我们一般需要的数据类型有比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等,操作系......
  • C++的第一课
    一、基本语法我们就直接跳过他的介绍了,大家如果想了解可以去官网看看,那么我们就直接进入正题。#include<iostream>usingnamespacestd;intmain(){cout<<"HelloWorld";return0;}那么我们先来看看最简单的HelloWorld的输出,其实呢C++和C的代码书写方式呢......
  • C++Builder XE2 lite精简版
    听说XE8要出了,我这刚从CB6转型到2010不久的,也是眼馋。奈何XE7太大了,平时也只是学习琢磨点小东西,网上找的2010、2011(XE1)的精简版用着挺好,就是感觉不那么稳定。这两天闲着没事做了一个C++BuilderXE2的精简版,测试能用,没有集成第三方控件,带boost库,fmx。纯C++的(顺便鄙视一下那些只做de......
  • 【重学c++primer】第五章第二节 深入浅出:左值和右值
    文章目录左值右值传统的左值和右值划分glvalueprvaluexvalue总结左值和右值的转换左值转右值decltype左值右值传统的左值和右值划分左值:英文为leftvalue,简写lvalue右值:英文为rightvalue,简写rvalue一个左一个右,这个左右的判定是针对什么呢?实际上是针对等......
  • C++实现计算器(菜鸡版*2)
    我写了两种,都是支持小数的(默认从左到右,请自行解括号)别喷我这个很菜,还要用户自己解括号。大部分计算器不都这样吗(包括Windows自带的),而且我还编了一个可以直观的看到公式的。话虽如此,但我还是会努力编出更好用的计算器的喜欢就收藏一下吧第一种:数字/运算符一个一个输入代码:......