C++中string类的模拟实现

news2024/9/20 3:02:10

目录

1.string类的结构

2.默认成员函数

2.1.默认构造函数

2.2拷贝构造函数 

2.3赋值运算符重载 

2.4析构函数

 3.迭代器(Iterators)

4.string类的空间操作(Capacity)

4.1size()

4.2capacity()

4.3clear() 

4.4reserve()

5.元素访问(Element access)

6.string类的修改操作(Modifiers) 

6.1push_back()

6.2append() 

6.3operator+=()

6.4swap()

6.5insert() 

6.6 erase()

7.字符串操作(String operations) 

7.1c_str()

7.2find()

7.3substr()

8.非成员函数重载(Non-member function overloads) 

8.1关系运算符(relational operators)

8.2输入输出重载(operator>> and operator<<) 

 8.2.1输出运算符重载

8.2.2输入运算符重载

9.参考代码

9.1string.h 

9.2string.cpp

9.3Test.cpp 


1.string类的结构

		char* _str = nullptr;
		size_t _capacity = 0;
		size_t _size = 0;
		//这里可以直接给默认值,相当于定义,因为有const,只有整型可以
		//static const size_t npos = -1;
		static const size_t npos;

        string类结构里有一个_str指针,指向存储字符的数组,_capacity表示当前string的空间大小,_size表示当前string中的有效元素个数,静态常量npos默认等于-1,表示整形的最大值。 

2.默认成员函数

2.1.默认构造函数

		//默认构造函数
		string()
			//直接给空指针在使用.c_str()时进行打印对空指针进行了解引用
			:_str(new char[1] {'\0'})
			//:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{}
		
		//带参的构造
		string(const char* str)
		{
			_size = strlen(str);
			//_capacity不包含\0
			_capacity = _size;
			//开空间的时候多开一个用于存储\0
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

        这里的默认构造函数不能给_str空指针,如果是个空串进行打印的话,会对空指针进行解引用会导致程序崩溃。 因为要存一个'\0',所以空串也要开一个空间。

        这里可以把上述两个构造合成一个构造函数。

//将上述两个构造函数合并成一个全缺省的构造函数
string(const char* str = "")	//空的常量字符串默认包含一个\0
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);	//strcpy先拷贝再判断,会拷贝\0
}

2.2拷贝构造函数 

        1.传统写法

//传统写法
string(const string& s)
{
	_str = new char[s._capacity + 1];    //多开一个空间给'\0'
	strcpy(_str, s._str);                //拷贝数据
	_size = s._size;
	_capacity = s._capacity;
}

        2.现代写法

void swap(string& s) 
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

//现代写法
string(const string& s)
{
	string tmp(s._str);
	swap(tmp);
}

        这里调用库里面的交换函数,交换string结构里面的数据。然后在拷贝函数中用被拷贝的string对象s中的_str构造一个临时对象,然后进行交换,函数结束之后这个临时对象自动销毁,构造出一个与s一样的新对象。

2.3赋值运算符重载 

         1.写法1

		string& operator=(const string& s)
		{
			if (this != &s)    //检测是否是自己给自己赋值
			{
				delete[] _str;    //释放原来对象的空间
				_str = new char[s._capacity + 1];    //new一个和s一样大的空间
				strcpy(_str, s._str);    //拷贝数据
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

        2.写法2 

		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}

			return *this;
		}

        这个和拷贝函数的现代写法思路一样。

        3.写法3

		string& operator=(string tmp)
		{
			swap(tmp);	//这里虽然tmp被交换了,但是形参的改变不影响实参
			return *this;
		}

        这里直接使用传值传参,然后进行交换,函数结束之后并不会影响实参。 

2.4析构函数

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

 3.迭代器(Iterators)

		// iterator
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const    //函数重载,重载迭代器用于const对象
		{
			return _str;
		}

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

         因为string的底层是利用数组进行存储的,所以这里直接利用原始指针作为迭代器即可,用两个typedef关键字将char*类型和const char*类型改为迭代器的名字。

4.string类的空间操作(Capacity)

4.1size()

		size_t size() const
		{
			return _size;
		}

4.2capacity()

		size_t capacity() const
		{
			return _capacity;
		}

4.3clear() 

         清除string对象里面的数据,但是这里不缩容_capacity不改变。

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

4.4reserve()

         该函数在string.cpp里面实现的,所以加上了类域限定符.

	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

5.元素访问(Element access)

        这里仅实现[]的重载。 

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

			return _str[index];
		}
		const char& operator[](size_t index)const
		{
			assert(index < _size);

			return _str[index];
		}

6.string类的修改操作(Modifiers

6.1push_back()

        尾插一个字符.

	void string::push_back(char c)
	{
		//扩容
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}

		_str[_size] = c;
		++_size;
		_str[_size] = '\0';
	}

6.2append() 

        尾插一个字符串.这里扩容保持一个对齐的原则,如果需要的空间大于原来空间的两倍,则需要多少开多少,如果小于原来的两倍,则开2倍.

	void string::append(const char* str)
	{
		size_t len = strlen(str);    //计算尾插的str大小
		if (_size + len > _capacity)
		{
			//需要的空间大于原空寂的2倍,需要多少开多少,小于2倍开2倍
			reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));
		}

		strcpy(_str + _size, str);
		_size += len;
	}

6.3operator+=()

        +=运算符的重载实现能加一个字符,也能加一个字符串,复用上述两个接口进行实现.

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

6.4swap()

		void swap(string& s) 
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

6.5insert() 

	// 在pos位置上插入字符c/字符串str
	void string::insert(size_t pos, char c)
	{
		assert(pos <= _size);

		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		//这里用size_t类型头插会出错,因为end永远都小于不了0
		//size_t end = _size;	//从最后一个\0开始往后挪动数据
		//用int end在与pos比较时会提升为size_t类型,也会出错
		//int end = _size;
		//while (end >= (int)pos)
		//{
		//	_str[end + 1] = _str[end];
		//	end--;
		//}

		//挪动数据,_size处是\0,从后面的\0开始挪动
		size_t end = _size + 1;
		while (end > pos)    
		{
			_str[end] = _str[end - 1];
			end--;
		}

		_str[pos] = c;
		++_size;
	}
	//在pos位置插入字符串
    void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);

		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			//大于2倍,需要多少扩多少,小于2倍,扩2倍
			reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));
		}
        
        //挪动数据
		size_t end = _size + len;
		while (end >= pos + len - 1)
		{
			_str[end] = _str[end - len];
			--end;
		}
        //插入字符串
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}

		_size += len;
	}

6.6 erase()

        如果需要删除的len的长度大于从pos到最后位置的长度,则修正len之后进行删除.

	// 删除pos位置上之后的len个元素
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
        
        //如果删除的元素个数大于从pos到最后的个数,修正一下len
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;
		}
	}

7.字符串操作(String operations) 

7.1c_str()

        这里返回string对象中的数组指针,可以说是返回C语言类型的字符串对象.

		//返回string中的指向字符串的指针
		const char* c_str() const
		{
			return _str;
		}

7.2find()

	// 返回c在string中第一次出现的位置
	size_t string::find(char c, size_t pos) const
	{
		assert(pos < _size);

		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}
        
        //没找到返回最大的整数
		return npos;
	}

	// 返回子串s在string中第一次出现的位置
	size_t string::find(const char* s, size_t pos) const
	{
		assert(pos < _size);

        //调用库里面的strstr函数在自身中寻找子串
		const char* ptr = strstr(_str + pos, s);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}

7.3substr()

	//返回子串
	string string::substr(size_t pos, size_t len) const
	{
		assert(pos < _size);
        
		//len大于剩余字符的长度,更新一下len
		if (len > _size - pos)
		{
			len = _size - pos;
		}

		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}

		return sub;
	}

8.非成员函数重载(Non-member function overloads) 

8.1关系运算符(relational operators)

        字符串的关系运算符与C语言中的compare()类似,这里直接复用库里面的compare()函数.

需要注意的是compare()函数是使用C语言中的字符串格式进行比较的,这里比较的是string对象中_str指向的数组.

//relational operators
	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 s1 < s2 || s1 == s2;
	}
	bool operator>(const string s1, const string& s2)
	{
		return !(s1 <= s2);
	}
	bool operator>=(const string s1, const string& s2)
	{
		return !(s1 < s2);
	}
	bool operator!=(const string s1, const string& s2)
	{
		return !(s1 == s2);
	}

8.2输入输出重载(operator>> and operator<<) 

         这里的输入和输出为什么重载成全局函数请参考C++类和对象(5)--日期类的实现中友元声明中的注释.

 8.2.1输出运算符重载

        遍历string对象一个一个输出即可.

	ostream& operator<<(ostream& _cout, const string& s)
	{
		for (auto ch : s)
		{
			_cout << ch;
		}

		return _cout;
	}

8.2.2输入运算符重载

        这里首先先清除s中原有的数据,然后在栈里面开一个256大小的buff(为了减少扩容的次数).这里用get()函数一个一个读取输入的字符,如果用输入运算符的话会忽略输入的空格和换行符.当一个buff满了之后拷贝到s中,跳出循环后如果buff中还有遗留的数据,则全部拷贝到s中.

	istream& operator>>(istream& _cin, string& s)
	{
		s.clear();

		const int N = 256;
		char buff[N];
		int i = 0;

		char ch;
		//in >> ch; 默认会忽略空格和换行符
		ch = _cin.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;

				i = 0;
			}
			//in >> ch;
			ch = _cin.get();
		}

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

		return _cin;
	}

9.参考代码

        string实现在XiaoC这个命名空间中,上述的代码没有做测试,下面给出测试代码,有兴趣可以自行对接口进行测试.

9.1string.h 

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
#include <string>
using namespace std;

namespace XiaoC
{
	class string
	{
		//friend ostream& operator<<(ostream& _cout, const XiaoC::string& s);
		//friend istream& operator>>(istream& _cin, XiaoC::string& s);
	public:
		默认构造函数
		//string()
		//	//直接给空指针在使用.c_str()时进行打印对空指针进行了解引用
		//	:_str(new char[1] {'\0'})
		//	//:_str(nullptr)
		//	,_size(0)
		//	,_capacity(0)
		//{}
		//
		带参的构造
		//string(const char* str)
		//{
		//	_size = strlen(str);
		//	//_capacity不包含\0
		//	_capacity = _size;
		//	//开空间的时候多开一个用于存储\0
		//	_str = new char[_capacity + 1];
		//	strcpy(_str, str);
		//}

		//将上述两个构造函数合并成一个全缺省的构造函数
		string(const char* str = "")	//空的常量字符串默认包含一个\0
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);	//strcpy先拷贝再判断,会拷贝\0
		}


		//拷贝构造
		//传统写法
		//string(const string& s)
		//{
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}

		//现代写法
		string(const string& s)
		{
			string tmp(s._str);
			swap(tmp);
		}

		//赋值重载
		//写法1
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		delete[] _str;
		//		_str = new char[s._capacity + 1];
		//		strcpy(_str, s._str);
		//		_size = s._size;
		//		_capacity = s._capacity;
		//	}
		//	return *this;
		//}

		//写法2
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);
		//		swap(tmp);
		//	}
		//	return *this;
		//}

		//写法3
		string& operator=(string tmp)
		{
			swap(tmp);	//这里虽然tmp被交换了,但是形参的改变不影响实参
			return *this;
		}

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

		// iterator
		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;
		}

		// modify
		void push_back(char c);
		void append(const char* str);
		string& operator+=(char c);
		string& operator+=(const char* str);

		void swap(string& s) 
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//返回string中的指向字符串的指针
		const char* c_str() const
		{
			return _str;
		}

		// capacity
		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}

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

		void reserve(size_t n);

		// access
		char& operator[](size_t index)
		{
			assert(index < _size);

			return _str[index];
		}
		const char& operator[](size_t index)const
		{
			assert(index < _size);

			return _str[index];
		}

		// 返回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/字符串str
		void insert(size_t pos, char c);
		void insert(size_t pos, const char* str);

		// 删除pos位置上的元素,并返回该元素的下一个位置
		void erase(size_t pos, size_t len = npos);

		//返回子串
		string substr(size_t pos = 0, size_t len = npos) const;

	private:
		char* _str = nullptr;
		size_t _capacity = 0;
		size_t _size = 0;
		//这里可以直接给默认值,相当于定义,因为有const,只有整型可以
		//static const size_t npos = -1;
		static const size_t npos;
	};

	//relational operators
	bool operator<(const string& s1, const string& s2);
	bool operator==(const string s1, const string& s2);
	bool operator<=(const string s1, const string& s2);
	bool operator>(const string s1, const string& s2);
	bool operator>=(const string s1, const string& s2);
	bool operator!=(const string s1, const string& s2);

	ostream& operator<<(ostream& _cout, const string& s);
	istream& operator>>(istream& _cin, string& s);

}

9.2string.cpp

#include "string.h"
namespace XiaoC
{
	const size_t string::npos = -1;

	void string::push_back(char c)
	{
		//扩容
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}

		_str[_size] = c;
		++_size;
		_str[_size] = '\0';
	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			//大于2倍,需要多少开多少,小于2倍开2倍
			reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));
		}

		strcpy(_str + _size, str);
		_size += len;
	}

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

	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	// 在pos位置上插入字符c/字符串str
	void string::insert(size_t pos, char c)
	{
		assert(pos <= _size);

		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		//这里用size_t类型头插会出错,因为end永远都小于不了0
		//size_t end = _size;	//从最后一个\0开始往后挪动数据
		//用int end在与pos比较时会提升为size_t类型,也会出错
		//int end = _size;
		//while (end >= (int)pos)
		//{
		//	_str[end + 1] = _str[end];
		//	end--;
		//}

		//挪动数据,_size处是\0,从后面的\0开始挪动
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			end--;
		}

		_str[pos] = c;
		++_size;
	}

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

		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			//大于2倍,需要多少扩多少,小于2倍,扩2倍
			reserve((_size + len) > (2 * _capacity) ? (_size + len) : (2 * _capacity));
		}

		size_t end = _size + len;
		while (end >= pos + len - 1)
		{
			_str[end] = _str[end - len];
			--end;
		}

		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}

		_size += len;
	}

	// 删除pos位置上之后的len个元素
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;
		}
	}

	// 返回c在string中第一次出现的位置
	size_t string::find(char c, size_t pos) const
	{
		assert(pos < _size);

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

		return npos;
	}

	// 返回子串s在string中第一次出现的位置
	size_t string::find(const char* s, size_t pos) const
	{
		assert(pos < _size);

		const char* ptr = strstr(_str + pos, s);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}

	//返回子串
	string string::substr(size_t pos, size_t len) const
	{
		assert(pos < _size);

		//len大于剩余字符的长度,更新一下len
		if (len > _size - pos)
		{
			len = _size - pos;
		}

		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}

		return sub;
	}

	//relational operators
	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 s1 < s2 || s1 == s2;
	}
	bool operator>(const string s1, const string& s2)
	{
		return !(s1 <= s2);
	}
	bool operator>=(const string s1, const string& s2)
	{
		return !(s1 < s2);
	}
	bool operator!=(const string s1, const string& s2)
	{
		return !(s1 == s2);
	}

	ostream& operator<<(ostream& _cout, const string& s)
	{
		for (auto ch : s)
		{
			_cout << ch;
		}

		return _cout;
	}
	istream& operator>>(istream& _cin, string& s)
	{
		s.clear();

		const int N = 256;
		char buff[N];
		int i = 0;

		char ch;
		//in >> ch; 默认会忽略空格和换行符
		ch = _cin.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;

				i = 0;
			}
			//in >> ch;
			ch = _cin.get();
		}

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

		return _cin;
	}
}

9.3Test.cpp

#include "string.h"

namespace XiaoC
{
	//测试构造函数
	void test_string1()
	{
		string s1;
		string s2("hello world");

		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
	}

	//测试遍历访问
	void test_string2()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		//[] + 下标访问
		for (size_t i = 0; i < s1.size(); i++)
		{
			s1[i] += 2;
		}

		cout << s1.c_str() << endl;

		//范围for底层就是替代为迭代器
		for (auto e : s1)
		{
			cout << e << " ";
		}
		cout << endl;

		//通过迭代器遍历访问
		string::iterator it = s1.begin();
		while (it != s1.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	//测试增删改查
	void test_string3()
	{
		string s1 = "hello world";
		s1 += 'x';
		s1 += 'c';

		cout << s1.c_str() << endl;

		s1 += " hello XiaoC";
		cout << s1.c_str() << endl;

		string s2 = "abcd";
		cout << s2.c_str() << endl;
		s2.insert(0, 'c');
		cout << s2.c_str() << endl;

		string s3 = "hello world";
		cout << s3.c_str() << endl;
		s3.insert(0, "xxx");
		cout << s3.c_str() << endl;

		string s4 = "hello world";
		cout << s4.c_str() << endl;
		s4.erase(0, 3);
		cout << s4.c_str() << endl;

		string s("test.cpp.zip");
		size_t pos = s.find('.');
		string suffix = s.substr(pos);
		cout << suffix.c_str() << endl;

		//拷贝构造
		string copy(s);
		cout << copy.c_str() << endl;

		//赋值运算符
		string s5 = "XiaoC";
		s5 = s;
		cout << s5.c_str() << endl;
	}

	//测试比较大小
	void test_string4()
	{
		string s1 = "helloworld";
		string s2 = s1;
		cout << (s1 == s2) << endl;
		cout << (s1 < s2) << endl;
	}

	//测试输入输出
	void test_string5()
	{
		string s1 = "hello world";
		cout << s1 << endl;

		string s2;
		cin >> s2;
		cout << s2 << endl;
	}

	//测试返回字串
	void test_string6()
	{
		string s1 = "hello world";
		string s2;
		s2 = s1.substr(6, 5);
		cout << s2 << endl;
	}
}
int main()
{
	//XiaoC::test_string1();
	//XiaoC::test_string2();
	//XiaoC::test_string3();
	//XiaoC::test_string4();
	XiaoC::test_string5();
	//XiaoC::test_string6();

	return 0;
}

 

 

 

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

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

相关文章

【ARM】中断的处理

ARM的异常向量表 如果发生异常后并没有exception level切换&#xff0c;并且发生异常之 前使用的栈指针是SP_EL0&#xff0c;那么使用第一组异常向量表。如果发生异常后并没有exception level切换&#xff0c;并且发生异常之 前使用的栈指针是SP_EL1/2/3&#xff0c;那么使用第…

字节推音乐生成神器 Seed-Music 支持多样化输入和精确控制

Seed-Music&#xff0c;字节跳动的最新音乐创作神器&#xff0c;能通过文字、音频等多种方式轻松生成音乐&#xff0c;仿佛拥有魔法般的魔力。它巧妙地融合了自回归语言模型与扩散模型&#xff0c;不仅确保了音乐作品的高品质&#xff0c;还赋予了用户对音乐细节的精准掌控能力…

基于Python flask的淘宝商品数据分析可视化系统,包括大屏和主题分析,还有回归预测

背景介绍 随着电子商务的迅猛发展&#xff0c;平台上积累了大量的用户行为和商品交易数据。这些数据蕴含着极大的商业价值&#xff0c;可以为市场趋势预测、商品优化以及用户行为分析提供重要的参考。淘宝作为全球最大的在线购物平台之一&#xff0c;拥有海量的商品和用户数据…

OJ在线评测系统 思考主流OJ的实现方案 常用概念 自己的思考

OJ判题系统常用概念 OJ系统 在线判题系统 AC all accpeted 测试样例全部通过 题目介绍 题目输入 题目输出 题目输出用例 题目输入用例 不能让用户随便引入包 随便遍历 暴力破解 需要使用正确的算法 提交后不会立刻出结果 而是异步处理 提交后会生成一个提交记录 有运…

好用的ai写作有哪些?5个软件帮助你快速进行ai写作

好用的ai写作有哪些&#xff1f;5个软件帮助你快速进行ai写作 AI写作工具正变得越来越流行&#xff0c;能够帮助用户更快速、高效地完成各种写作任务&#xff0c;包括生成文章、写小说、改进语法等。以下是5个非常好用的AI写作软件&#xff0c;它们可以帮助你快速进行AI写作&a…

秋招八股总结

transformer 损失函数 交叉熵的原理 公式 xi是true_label&#xff0c;yi是神经网络预测为正确的概率 对比学习loss 对比学习损失函数 InfoNEC Loss&#xff08;bge中也用的这个&#xff09; SimCSE的主要思想&#xff1a;同一句话通过不同的drop out作为正例&#xff0…

腾讯百度阿里华为常见算法面试题TOP100(6):回溯、二分查找、二叉树

之前总结过字节跳动TOP50算法面试题&#xff1a; 字节跳动常见算法面试题top50整理_沉迷单车的追风少年-CSDN博客_字节算法面试题 回溯 46.全排列 class Solution { private:vector<vector<int> > ans;void dfs(vector<int>& nums, vector<int>&a…

西南民族大学若尔盖国家野外台站王志强研究员团队在树皮生态化学计量学研究中取得新进展!

本文首发于“生态学者”微信公众号&#xff01; 近日&#xff0c;西南民族大学四川若尔盖高寒湿地生态系统国家野外科学观测研究站王志强研究员团队在植物科学顶级期刊New Phytologist和环境科学与生态学TOP期刊Science of the Total Environment分别以“Global patterns and …

【Java】网络编程-地址管理-IP协议后序-NAT机制-以太网MAC机制

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a; c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 &#x1f434…

AI大模型之旅-大模型为的微调参数详解

output model.generate( inputs.input_ids, max_lengthmax_length, do_sampledo_sample, temperaturetemperature, top_ptop_p, top_ktop_k ) 大模型文本常用参数为以上几个&#xff0c;下面我们详细解析下&#xff1a; 初解&#xff1a; max_length 定义&#xff1a;生成的…

MacOS安装homebrew,jEnv,多版本JDK

1 安装homebrew homebrew官网 根据官网提示&#xff0c;运行安装命令 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"安装后&#xff0c;bash会提示执行两条命令 (echo; echo eval "$(/opt/homebrew/b…

简明linux系统编程--共享内存消息队列信号量

目录 1.父子进程共享内存 1.1基本说明 1.2主要步骤 1.3shmget函数介绍​编辑 1.4函数返回值 1.5shmat函数介绍 1.6shmdt函数介绍 1.7结合代码理解 2.非亲缘关系的进程的共享内存通信 2.1和上面的区别 2.2如何通信 2.3具体代码 3.父子进程消息队列 4.非亲缘关系的进…

极狐GitLab 重要安全版本:17.3.3, 17.2.7, 17.1.8, 17.0.8, 16.11.10

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

BitLocker硬盘加密的详细教程分享

硬盘加密是将数据转换为一种只有授权用户才能读取的形式。通过使用加密算法&#xff0c;硬盘上的数据在存储时被加密&#xff0c;只有输入正确的密钥或密码才能解密和访问这些数据。 硬盘加密的重要性 数据是现代社会的重要资产&#xff0c;保护这些数据免受非法访问和窃取至关…

Mobile net V系列详解 理论+实战(2)

Mobilenet 系列 实践部分一、数据集介绍二、模型整体框架三、模型代码详解四、总结 实践部分 本章针对实践通过使用pytorch一个实例对这部分内容进行吸收分析。本章节采用的源代码在这里感兴趣的读者可以自行下载操作。 一、数据集介绍 可以看到数据集本身被存放在了三个文件…

处理RabbitMQ连接和认证问题

在使用RabbitMQ进行消息队列管理时&#xff0c;我们可能会遇到各种连接和认证问题。本文将介绍如何诊断和解决这些问题&#xff0c;并通过使用RabbitMQ的管理端进行登录验证来确保配置正确。 1. 问题概述 在最近的一次部署中&#xff0c;我们遇到了两个主要问题&#xff1a; …

一对一,表的设计

表很大&#xff0c;比如用户 用户登录只需要部分数据&#xff0c;所以把用户表拆成两个表 用户登录表 用户信息表 一对一设计有两种方案&#xff1a; 加外键&#xff0c;唯一 主键共享

学生考试成绩老师发布平台

老师们一直肩负着传授知识与评估学生学习成果的双重责任。其中&#xff0c;发布学生考试成绩是教学过程中不可或缺的一环。然而&#xff0c;传统的成绩发布方式往往繁琐且耗时。老师们需要手动整理成绩&#xff0c;然后通过电话、短信或电子邮件逐一通知学生和家长&#xff0c;…

Jenkins设置自动拉取代码后怎么设置自动执行构建任务?

在 Jenkins 中设置自动拉取代码后&#xff0c;可以通过以下步骤设置自动执行构建任务&#xff1a; 一、配置构建触发器 打开已经设置好自动拉取代码的 Jenkins 任务。在 “构建触发器” 部分&#xff0c;除了 “Poll SCM”&#xff08;用于定时检查代码仓库更新&#xff09;外…

Mybatis 和 数据库连接

第一次要下载驱动 查询数据库版本 但是在idea查看数据库我不行&#xff0c;插件我也装了&#xff0c;然后我在尝试改版本。也不行。 爆错 感觉还是插件的问题。先不弄了&#xff0c;影响不大。 但是加载了这个&#xff0c;能在idea写sql语句&#xff0c;还能有提示。