【C++初阶】六、STL---string模拟实现

news2024/10/5 16:32:56

目录

一、模拟实现接口总览

二、string模拟实现

2.1 构造函数

2.2 析构函数

2.3 拷贝构造函数

2.3.1 传统写法

2.3.2 现代写法

2.4 赋值运算符重载

2.4.1 传统写法

2.4.2 现代写法

2.5 iterator

2.5.1 begin

2.6 Capacity

2.6.1 size

2.6.2 capacity

2.6.2 empty

2.6.3 reserve

2.6.4 resize

2.6.5 clear

2.7 Element access

2.7.1 operator[ ]

2.8 Modify

2.8.1 push_back

2.8.2 append

2.8.3 operator+=

2.8.4 swap

2.9 String operations

2.9.1 c_str

2.9.2 find

2.9.3 insert

2.9.3 erase

2.10 Non-member function overloads

2.10.1 operator<<

2.10.2 operator>>

2.10.3 relational operators(string)

三、string模拟实现代码


一、模拟实现接口总览

Member functions 

Member functions
    //构造函数
		string(const char* str = "")
    //析构函数
		~string()
    //拷贝构造 -- 现代写法
		string(const string& s)
    //赋值重载 -- 现代写法2
		string& operator=(string s)

//iterators
    iterator begin()
    iterator end()

//Capacity
    size_t size()const
    size_t capacity()const
    bool empty()const
    //更改容量大小
		void reserve(size_t n)
    //调整字符串大小
		void resize(size_t n, char ch = '\0')
    //清空字符串
		void clear()

//Element access
    char& operator[](size_t pos)
    const char& operator[](size_t pos)const

//modify
    //尾插一个字符
		void push_back(char c)
    //尾插一个字符串
		void append(const char* str)
    //+= 一个字符
		string& operator+=(char c)
    //+= 一个字符串
		string& operator+=(const char* str)
    //交换两个字符串
		void swap(string& s)

//String operations
    const char* c_str()const
    // 返回字符c 在string中第一次出现的位置
		size_t find(char c, size_t pos = 0)const
    // 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0)const
    //在pos位置上插入字符c,并返回该字符的位置
		string& insert(size_t pos, char c)
    //在pos位置上插入字符串s,并返回该字符的位置
		string& insert(size_t pos, const char* s)
    //删除pos位置上的元素,并返回该元素的下一个位置
		string& erase(size_t pos, size_t len = npos)

Non-member function overloads

Non-member function overloads
    ostream& operator<<(ostream& out, const string& s)
    istream& operator>>(istream& in, string& s)

        上面只是挑一些常用的进行模拟实现,要把自己实现的写在自己命名的命名空间里面,否则与库中的 string 会产生冲突

注:string 类模拟实现,最主要是实现 string 类的构造、拷贝构造、赋值运算符重载以及析构函数,面试也经常喜欢考这几个

二、string模拟实现

2.1 构造函数

        构造函数的参数设置成缺省参数,不传参默认构造空字符串

//构造函数
string(const char* str = "")
{
    _size = strlen(str);//字符串大小
    _capacity = _size;//构造时,容量大小默认与字符串大小相同
    _str = new char[_capacity + 1];//为字符串开辟空间(多开一个用于存放'\0')
    strcpy(_str, str);//将C字符串拷贝到已开好的空间
}

2.2 析构函数

        string 类的析构函数需要我们自己写,因为每个 string 类对象都开辟有一块空间,不释放会造成内存泄漏

//析构函数
~string()
{
    delete[] _str; //释放_str指向的空间
    _str = nullptr;
    _size = _capacity = 0;
}

2.3 拷贝构造函数

这里需要注意浅拷贝和深拷贝的问题

浅拷贝:

        也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规

        简单说就是 新拷贝出来的对象的指针 和 原对象的指针 指向的内存空间是同一块空间,其中一个对象的改动会对另一个对象造成影响,进行析构的时候会对同一份空间释放两次,造成非法访问

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

        深拷贝:原对象 与 新拷贝对象 互相独立,其中任何一个对象的改动不会对另外一个对象造成影响 

        很明显,string 类的拷贝构造需要深拷贝,拷贝构造有两种写法,传统写法和现代写法,一般比较推荐现代写法

2.3.1 传统写法

        开辟一块和原对象空间大小一样的空间,然后将原对象的字符串拷贝过去,接着把原对象的其他成员变量也赋值过去即可

//拷贝构造函数
//传统写法
string(const string& s)
{
	_size = s._size;
	_capacity = s._capacity;
	_str = new char[_capacity + 1];//申请空间
	strcpy(_str, s._str);//拷贝字符串到新空间
}

2.3.2 现代写法

        现代写法与传统写法的思想不同:复用构造函数构造一个 tmp对象,对 tmp 初始化的对象是原对象的字符串,然后再将 tmp对象 与 新拷贝对象的数据交换即可,新拷贝对象先用初始化列表设置为空,新拷贝对象并没有开辟空间。新拷贝对象的 _str 与原对象的 _str 指向的也不是同一块空间,是互相独立的

//拷贝构造 -- 现代写法
string(const string& s)
	:_str(nullptr)
	, _size(0)
	,_capacity(0)
	{
		string tmp(s._str);//复用构造函数,构造 tmp对象
		swap(tmp);//交换
	}

 swap 函数是后面实现的,并不是使用库中的 swap

2.4 赋值运算符重载

        与拷贝构造函数类似,赋值运算符重载函数的模拟实现也涉及深浅拷贝问题,赋值运算符重载也是进行深拷贝

2.4.1 传统写法

        赋值运算符重载函数的传统写法与拷贝构造函数的传统写法类似,只是左值的 _str 在开辟新空间之前需要先将原来的空间释放掉,并且在进行操作之前还需判断是否是自己给自己赋值,若是自己给自己赋值,则无需进行任何操作

//赋值重载
//传统写法
string& operator=(const string& s)
{
	if (this == &s)//检查自我赋值
	{
		return *this;
	}

	delete[] _str;
	_size = s._size;
	_capacity = s._capacity;
	_str = new char[_capacity + 1];
	strcpy(_str, s._str);

	return *this;//返回左值,目的是为了支持连续赋值
}

2.4.2 现代写法

现代写法 -- 1

        复用拷贝构造函数,拷贝构造一个 tmp对象,对 tmp 初始化的对象是原对象,然后将该 tmp对象与 新拷贝对象进行交换交换,图就不画了,与上面的拷贝构造现代写法图类似

//赋值重载 -- 现代写法1
string& operator=(const string& s)
{
	if (this == &s)//检查自我赋值
	{
		return *this;
	}
	string tmp(s);//复用拷贝构造函数,用s拷贝构造出对象tmp
	swap(tmp);

	return *this;
}

现代写法 -- 2

        也是复用拷贝构造,但是参数不再是传引用传参,而是传值形参,而传值传参则会自动调用拷贝构造函数,但是这种写法无法检查自我赋值的情况,但是这种情况几乎不会出现,推荐这种写法

 //赋值重载 -- 现代写法2
string& operator=(string s)//传值传参自动调用拷贝构造,比如,s1 = s2,先把s2拷贝到s,s再交换给this,也就是s1
{
	swap(s);
	return *this;
}

2.5 iterator

        string 类中的迭代器实际上就是指针,只是给指针起了一个别名叫 iterator 而已

typedef char* iterator;//迭代器

2.5.1 begin

直接返回字符串第一个元素的地址

iterator begin()
{
	return _str;
}

2.5.2 end

直接返回字符串中最后一个字符的后一个字符的地址(即’\0’的地址)

iterator end()
{
	return _str + _size;
}

范围 for 的底层就是迭代器

2.6 Capacity

2.6.1 size

size 用于获取字符串当前的有效长度(不包括’\0’)

size_t size()const
{
	return _size;
}

2.6.2 capacity

capacity函数用于获取字符串当前的容量

size_t capacity()const
{
	return _capacity;
}

2.6.2 empty

判断有效字符串是否为空,为空返回 true,否则返回 false

bool empty()const
{
	return _size == 0;
}

2.6.3 reserve

reserve 规则: 

  • n 大于原字符串的 capacity,此时 reserve 函数会将 capacity 扩容到 n
  • n 小于等于原字符串的 capacity,capacity 容量不变 (不缩容)
//更改容量大小
void reserve(size_t n)
{
	if (n > _capacity)//n大于现有容量,增容,n小于现有容量则不改变容量
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;//更新容量
	}
}

2.6.4 resize

resize 规则:

  1. n 小于原字符串的 size,此时 resize 函数会将原字符串的 size 改为 n,但不会改变 capacity
  2. 大于原字符串的 size,但小于其 capacity,此时 resize 函数会将 size 后面的空间全部设置为字符 c 或默认的‘\0’
  3. 大于原字符串的 capacity,此时 resize 函数会将原字符串扩容,然后将size 后面的空间全部设置为字符 c 默认的‘\0’
//调整字符串大小
		void resize(size_t n, char ch = '\0')
		{
			if (n > _size)//n大于原字符串长度,增容,末尾追加字符
			{
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
			else//小于等于
			{
				_str[n] = '\0';
				_size = n;
			}
		}

2.6.5 clear

clear 函数用于清空有效字符串

//清空有效字符串
void clear()
{
	_str[0] = '\0';
	_size = 0;
}

2.7 Element access

2.7.1 operator[ ]

        [ ]运算符的重载是为了让 string对象能像C字符串一样,通过 [ ] +下标 的方式获取字符串对应位置的字符,就像数组可以可以通过下标访问元素

//可读可写
char& operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}

//只读
const char& operator[](size_t pos)const
{
	assert(pos < _size);
	return _str[pos];
}

2.8 Modify

2.8.1 push_back

        push_back函数的作用就是在当前字符串的后面尾插上一个字符,尾插之前首先需要判断是否需要增容,若需要,则调用reserve函数进行增容,然后再尾插字符,注意尾插完字符后需要在该字符的后方设置上’\0’,否则打印字符串的时候会出现非法访问,因为尾插的字符后方不一定就是’\0’

//尾插一个字符
void push_back(char c)
	{
		if (_size == _capacity)//判断是否需要增容
		{
			size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newCapacity);
		}
		_str[_size] = c;
		++_size;
		_str[_size] = '\0';
	}

2.8.2 append

        append 函数的作用是在当前字符串的后面尾插一个字符串

	//尾插一个字符串
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)//判断是否需要增容
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);//将str尾插到字符串后面
			_size += len;
		}

2.8.3 operator+=

+= 一个字符直接复用 push_back

//+= 一个字符
string& operator+=(char c)
{
	push_back(c);
	return *this;
}

+= 一个字符串直接复用 append

//+= 一个字符串
string& operator+=(const char* str)
{
	append(str);
	return *this;
}

2.8.4 swap

        swap 函数用于交换两个对象的数据,直接调用库里的 swap 模板函数将对象的各个成员变量进行交换即可

//交换两个字符串
void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

2.9 String operations

2.9.1 c_str

c_str 函数用于获取对象C类型的字符串

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

2.9.2 find

在字符串中查找一个字符

// 返回字符c 在string中第一次出现的位置
size_t find(char c, size_t pos = 0)const
{
	assert(pos < _size);
	while (pos < _size)
	{
		if (_str[pos] == c)
		{
			return pos;//找到目标字符,返回其下标
		}
		pos++;
	}
	return npos;//没有找到目标字符,返回npos
}

在字符串中查找一个子串

// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0)const
{
	assert(pos < _size);
	const char* ptr = strstr(_str + pos, s);
	if (ptr == nullptr)//没有找到
	{
		return npos;
	}
	else
	{
		return ptr - _str;//返回字符串第一个字符的下标
	}
}

2.9.3 insert

        insert函数的作用是在字符串的任意位置插入字符或是字符串

任意位置插入字符:

//在pos位置上插入字符c,并返回该字符的位置
		string& insert(size_t pos, char c)
		{
			assert(pos <= _size);
			if (_size == _capacity)//判断是否需要增容
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}

			size_t end = _size + 1;
			while (pos < end)//挪动数据
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			_size++;

			return *this;
		}

任意位置插入字符串:

/在pos位置上插入字符串s,并返回该字符的位置
		string& insert(size_t pos, const char* s)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (pos + len - 1 < end)
			{
				_str[end] = _str[end - len];
				end--;
			}
			strncpy(_str + pos, s, len);
			_size += len;

			return *this;
		}

2.9.3 erase

erase函数的作用是删除字符串任意位置开始的 len 个字符,分两种情况:

  1. pos位置及其之后的有效字符都需要被删除
  2. pos位置及其之后的有效字符只需删除一部分
//删除pos位置上 len 个的元素
string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);
	if (len == npos || pos + len >= _size)//说明pos位置及其后面的字符都被删除
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else//说明pos位置及其后方的有效字符需要保留一部分
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}

	return *this;
}

2.10 Non-member function overloads

2.10.1 operator<<

        重载 <<运算符是为了让string对象能够像内置类型一样使用 <<运算符直接输出打印,实现直接使用对象进行遍历即可输出

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

2.10.2 operator>>

        重载 >>运算符是为了让 string对象能够像内置类型一样使用>>运算符直接输入,输入前我们需要先将对象的C字符串置空,然后从标准输入流读取字符,直到读取到 ’ ‘ 或是 ’\n’ 便停止读取

	istream& operator>>(istream& in, string& s)
	{
		s.clear();//清空
		char buff[128] = { '\0' };
		size_t i = 0;
		char ch = in.get();//读取一个字符
		while (ch != ' ' && ch != '\n')//当读取到的字符不是空格或'\n'的时候继续读取
		{
			if (i == 127)//满了
			{
				s += buff;
				i = 0;
			}
			buff[i++] = ch;
			ch = in.get();//继续读取字符
		}

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

		return in;
	}

2.10.3 relational operators(string)

        关系运算符有 >、>=、<、<=、==、!= 这六个,但是对于 C++中任意一个类的关系运算符重载,我们均只需重载其中的两个,剩下的四个关系运算符可以通过复用已经重载好了的两个关系运算符来实现

三、string模拟实现代码

(1)string.h

#pragma once

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

namespace fy
{
	class string
	{
	public:
		//构造函数
		string(const char* str = "")
		{
			_size = strlen(str);//字符串大小
			_capacity = _size;//构造时,容量大小默认与字符串大小相同
			_str = new char[_capacity + 1];//为字符串开辟空间(多开一个用于存放'\0')
			strcpy(_str, str);//将C字符串拷贝到已开好的空间
		}
		//析构函数
		~string()
		{
			delete[] _str; //释放_str指向的空间
			_str = nullptr;
			_size = _capacity = 0;
		}
		//拷贝构造函数
		传统写法
		//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);//复用构造函数,构造 tmp对象
			swap(tmp);//交换
		}

		//赋值重载
		传统写法
		//string& operator=(const string& s)
		//{
		//	if (this == &s)//检查自我赋值
		//	{
		//		return *this;
		//	}

		//	delete[] _str;
		//	_size = s._size;
		//	_capacity = s._capacity;
		//	_str = new char[_capacity + 1];
		//	strcpy(_str, s._str);
		// 
		//	return *this;//返回左值,目的是为了支持连续赋值
		//}

		赋值重载 -- 现代写法1
		//string& operator=(const string& s)
		//{
		//	if (this == &s)//检查自我赋值
		//	{
		//		return *this;
		//	}
		//	string tmp(s);//复用拷贝构造函数,用s拷贝构造出对象tmp
		//	swap(tmp);
		//	return *this;
		//}

		 //赋值重载 -- 现代写法2
		string& operator=(string s)//传值传参自动调用拷贝构造,比如,s1 = s2,先把s2拷贝到s,s再交换给this,也就是s1
		{
			swap(s);
			return *this;
		}

		//-------------------------------------------------------------------
		//iterators

		typedef char* iterator;//迭代器

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//-------------------------------------------------------------------
		//Capacity

		size_t size()const
		{
			return _size;
		}

		size_t capacity()const
		{
			return _capacity;
		}

		bool empty()const
		{
			return _size == 0;
		}

		//更改容量大小
		void reserve(size_t n)
		{
			if (n > _capacity)//n大于现有容量,增容,n小于现有容量则不改变容量
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;//更新容量
			}
		}

		//调整字符串大小
		void resize(size_t n, char ch = '\0')
		{
			if (n > _size)//n大于原字符串长度,增容,末尾追加字符
			{
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
			else//小于等于
			{
				_str[n] = '\0';
				_size = n;
			}
		}

		//清空有效字符串
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

		//-------------------------------------------------------------------
		//Element access

		//可读可写
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		//只读
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

		//-------------------------------------------------------------------
		//modify

		//尾插一个字符
		void push_back(char c)
		{
			if (_size == _capacity)//判断是否需要增容
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}
			_str[_size] = c;
			++_size;
			_str[_size] = '\0';
		}

		//尾插一个字符串
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)//判断是否需要增容
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);//将str尾插到字符串后面
			_size += len;
		}

		//+= 一个字符
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}

		//+= 一个字符串
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//交换两个字符串
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//-------------------------------------------------------------------
		//String operations

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

		// 返回字符c 在string中第一次出现的位置
		size_t find(char c, size_t pos = 0)const
		{
			assert(pos < _size);
			while (pos < _size)
			{
				if (_str[pos] == c)
				{
					return pos;//找到目标字符,返回其下标
				}
				pos++;
			}
			return npos;//没有找到目标字符,返回npos
		}

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0)const
		{
			assert(pos < _size);
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)//没有找到
			{
				return npos;
			}
			else
			{
				return ptr - _str;//返回字符串第一个字符的下标
			}
		}
		
		//在pos位置上插入字符c,并返回该字符的位置
		string& insert(size_t pos, char c)
		{
			assert(pos <= _size);
			if (_size == _capacity)//判断是否需要增容
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}

			size_t end = _size + 1;
			while (pos < end)//挪动数据
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			_size++;

			return *this;
		}

		//在pos位置上插入字符串s,并返回该字符的位置
		string& insert(size_t pos, const char* s)
		{
			assert(pos <= _size);
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			size_t end = _size + len;
			while (pos + len - 1 < end)
			{
				_str[end] = _str[end - len];
				end--;
			}
			strncpy(_str + pos, s, len);
			_size += len;

			return *this;
		}

		//删除pos位置上 len 个的元素
		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)//说明pos位置及其后面的字符都被删除
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else//说明pos位置及其后方的有效字符需要保留一部分
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}

			return *this;
		}

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

		const static size_t npos = -1;
	};

	//-------------------------------------------------------------------
	//Non-member function overloads

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

	istream& operator>>(istream& in, string& s)
	{
		s.clear();//清空
		char buff[128] = { '\0' };
		size_t i = 0;
		char ch = in.get();//读取一个字符
		while (ch != ' ' && ch != '\n')//当读取到的字符不是空格或'\n'的时候继续读取
		{
			if (i == 127)//满了
			{
				s += buff;
				i = 0;
			}
			buff[i++] = ch;
			ch = in.get();//继续读取字符
		}

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

		return in;
	}

	//relational operators(string)
	bool operator>(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) > 0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return operator==(s1, s2) || operator>(s1, s2);//复用
	}

	bool operator<(const string& s1, const string& s2)
	{
		return !operator>=(s1, s2);
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return !operator<(s1, s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !operator==(s1, s2);
	}
}

(2)Test.cpp

#define  _CRT_SECURE_NO_WARNINGS
#include "string.h"

void Test_string1()
{
	//构造
	fy::string s1;
	fy::string s2("abcd");
	//拷贝构造
	fy::string s3 = s2;
	fy::string s4(s2);
	//赋值重载
	s1 = s2;//编译器自动转换成 s1.operator(s2)
}

void Test_string2()//iterators
{
	fy::string s1("abcdef");
	fy::string::iterator it = s1.begin();
	for (it; it < s1.end(); ++it)
	{
		cout << *(it);
	}
	cout << endl;

	fy::string s2("12345");
	fy::string::iterator it2 = s2.begin();
	for (it2; it2 < s2.end(); ++it2)
	{
		cout << *(it2);
	}
	cout << endl;
}

void Test_string3()//Capacity
{
	fy::string s1("hello world");
	cout << s1.empty() << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	s1.reserve(5);
	cout << s1.capacity() << endl;
	s1.reserve(20);
	cout << s1.capacity() << endl;

	fy::string s2("hello world");
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;

	//s2.resize(6, 'x');
	s2.resize(15, 'x');//调试查看,<<这里暂时没有实现

	fy::string s3("aaaaa");
	s3.clear();
}

void Test_string4()//Element access
{
	fy::string s1("hello world");
	//可以使用 [] 访问字符串的每一位
	for (size_t i = 0; i < s1.size(); ++i)
	{
		cout << s1[i];
	}
	cout << endl;
}

void Test_string5()//modify
{
	fy::string s1("hello");
	s1.push_back(' ');
	s1.push_back('w');

	s1.append("orld");

	s1 += '!';
	s1 += "xxxx";
}

void Test_string6()//String operations
{
	fy::string s1("hello helloxxss");
	cout << s1.find('o') << endl;
	cout << s1.c_str() << endl;
	cout << s1.find('xxs') << endl;
	cout << s1.c_str() << endl;

	s1.insert(11, 'w');
	cout << s1.c_str() << endl;
	s1.insert(6, "xxxxxxxx");
	cout << s1.c_str() << endl;

	s1.erase(6, 10);
	cout << s1.c_str() << endl;

}

void Test_string7()//Non - member function overloads
{
	fy::string s1("hello");
	cout << s1 << endl;//测试<<

	fy::string s2;
	cin >> s2;//测试>>
	cout << s2 << endl;

	fy::string s3("aaa");
	fy::string s4("aaa");
	if (s3 == s4)
		cout << "s3==s4" << endl;
}

int main()
{
	Test_string7();
	return 0;
}

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maven的下载安装配置IDEA详细过程

1. 去官网下载好并且放在同一文件夹下面 下载maven安装包&#xff0c;解压即可使用 &#xff08;下载路径&#xff09;http://maven.apache.org/download.cgi 2. maven配置环境变量 MAVEN_HOME D:\IT\Java\apache-maven-3.8.4&#xff1a;这个写你自己放的目录下 进Path新增 %…

推荐系统之召回集读取服务

5.4 召回集读取服务 学习目标 目标 无应用 无 5.4.1 召回集读取服务 添加一个server的目录 会添加推荐中心&#xff0c;召回读取服务&#xff0c;模型排序服务&#xff0c;缓存服务这里先添加一个召回集的结果读取服务recall_service.pyutils.py中装有自己封装的hbase数据库读…

【GD32F427开发板试用】GD32的ISP进行程序烧录

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;羁傲不驯ぃ 什么是ISP? ISP是In-System Program的缩写&#xff0c;即在系统编程。通常我们开发使用仿真器来下载和调试程序&#xff0c;当固…

linux下安装elasticsearch

一&#xff1a;安装JDK1&#xff1a;java安装地址最新版&#xff1a;https://www.oracle.com/java/technologies/javase-downloads.html历史版&#xff1a;https://www.oracle.com/java/technologies/oracle-java-archive-downloads.html2&#xff1a;安装java(1)&#xff1a;安…

vue使用echarts画可视化大屏

画出来的页面效果如下&#xff1a; 一、布局 整体使用element-ui的layout布局&#xff0c;即el-rowel-col&#xff0c;便于自适应 二、配置跨域 首先创建个vue.config.js的文件 module.exports {lintOnSave: false,devServer: {proxy: { //配置跨域/: {target: , //填写…

Python——matplotlib绘图可视化知识点整理

无论你工作在什么项目上&#xff0c;IPython都是值得推荐的。利用ipython --pylab&#xff0c;可以进入PyLab模式&#xff0c;已经导入了matplotlib库与相关软件包&#xff08;例如Numpy和Scipy)&#xff0c;额可以直接使用相关库的功能。 本文作为学习过程中对matplotlib一些…

Oracle Blogs上的Flashback文章

Oracle Database und Temporal Validity Temporal Validity和Flashback的区别&#xff1f;两者通常配合使用。 延伸阅读&#xff1a;Oracle 数据库和时间有效性 时间有效性也称为 Flashback Time Travel。 显示不可见的列&#xff1a; set colinvisible on desc <table_…

PyQt5开发环境搭建 1.3 Python语法练习

第一组练习1阅读理解。输入红色框框中命令。说出文中大致意思。&#xff08;限30个字&#xff09;解&#xff1a;点击运行之后会跳到一个网站https://xkcd.com/353/练习2建立如下py文件并运行&#xff0c;贴出在Eric6下运行输出结果&#xff08;注意文件名中的bkjtest用自己的姓…

NSSCTF Round#7部分wp

Web ec_RCE 源码: <!-- A EZ RCE IN REALWORLD _ FROM CHINA.TW --> <!-- By 探姬 --> <?PHPif(!isset($_POST["action"]) && !isset($_POST["data"]))show_source(__FILE__);putenv(LANGzh_TW.utf8); $action $_POST["a…

layui遇到的一些问题

目录一、layui nav 菜单栏默认收缩二、layui 数据表格 单元格 颜色设置三、layui表格没有数据的时候&#xff0c;表头没有横向滚动条四、layui layer.open 弹窗全屏显示五、layui表格通过点击tr改变这一行的颜色六、 layer.open弹框弹出后父页面滚动问题七、LayUI下拉框中取值和…

Cuba勒索软件深度分析及防护建议

0 1. Cuba勒索软件的部署方式 Cuba勒索软件家族于2019年12月首次浮出水面。此后&#xff0c;该勒索软件家族背后的攻击者改变了策略和工具&#xff0c;成为2022年成为更普遍的威胁。该勒索软件历来通过Hancitor分发&#xff0c;通常通过恶意附件传递。 Hancitor也被称为Chani…

C++001-对比编程语言C++和python

文章目录C001-对比编程语言C和python编程语言发展史计算机 ENIAC机器语言&#xff1a;汇编语言&#xff1a;高级语言&#xff1a;机器汇编高级语言对比C语言与汇编不同高级语言的应用场景C和python语法对比Print Hello WorldPrint Hello 10 timesCreate a procedureCheck if li…

Kotlin的5种单例模式

前言最近在学习Kotlin这门语言&#xff0c;在项目开发中&#xff0c;运用到了单例模式。因为其表达方式与Java是不同的。所以对不同单例模式的实现进行了分别探讨。主要单例模式实现如下&#xff1a;饿汉式懒汉式线程安全的懒汉式双重校验锁式静态内部类式PS:该篇文章不讨论单例…

Windows并发测试工具

Apache安装目录cmd用ab并发测试工具&#xff0c;请求10次&#xff0c;并发为5ab -n 10 -c 5 http://www.ysbm.com/api.php/task/testBingfa

安装kali linuxnmap使用(一)

安装环境 vmware17 kali linux 怎么安装可以查看这个博主的文章 这么说你需要重置root密码 sudo passwd root 或者你打出node -v但是kali linux没有nodejs,则会询问你是否需要安装。开玩笑,你可以使用sudo 指令来获取权限(第一次输入需要你的密码) nmap 这是kali linux自带…

沁恒CH32V307单片机入门(02):官方库与工程模板介绍

文章目录目的官方库工程模板使用例程总结目的 现在开发单片机大多数时候都是面向库开发的&#xff0c;这里将简单介绍下CH32V307的官方库。 在开发过程中新建项目时通常会从某些模板开始&#xff0c;模板包含了库和初始化代码等内容&#xff0c;有一定的组织好的目录结构&…

【C++】模板

模板一、非类型模板参数二、模板的特化2.1 函数模板的特化2.2 类模板的特化2.2.1 全特化2.2.2 偏特化三、模板的分离编译一、非类型模板参数 模板参数分为类型形参与非类型形参。 类型形参&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称…

为什么 Go 不支持 []T 转换为 []interface

在 Go 中&#xff0c;如果 interface{} 作为函数参数的话&#xff0c;是可以传任意参数的&#xff0c;然后通过类型断言来转换。 举个例子&#xff1a; package mainimport "fmt"func foo(v interface{}) {if v1, ok1 : v.(string); ok1 {fmt.Println(v1)} else if…

【测试设计】使用jenkins 插件Allure生成自动化测试报告

前言 以前做自动化测试的时候一直用的HTMLTestRunner来生成测试报告&#xff0c;后来也尝试过用Python的PyH模块自己构建测试报告&#xff0c;在后来看到了RobotFramework的测试报告&#xff0c;感觉之前用的测试报告都太简陋&#xff0c;它才是测试报告应该有的样子。也就是在…

【01Studio MaixPy AI K210】25.云训练模型文件

采集数据 根据它云训练平台的要求&#xff0c;它要求的图片格式必须是224*224的&#xff08;重点之重点&#xff09;&#xff0c;所以可以利用K210跑脚本直接采集数据。 数据采集脚本 main.py实验名称&#xff1a;照相机 说明&#xff1a;通过按键拍照并在LCD上显示&#xff08…