STL-string(使用和部分模拟实现)

news2024/11/24 7:02:24

1.string

basic_string<char> 是 C++ 标准库中定义的一个模板类型,用于表示一个字符串。这个模板类接收一个字符类型作为模板参数。
typedef basic_string<char> string:string类是basic_string类模板的实例化,它使用 char作为其字符类型。

2.iterator迭代器

迭代器是类似指针的东西,但不一定是指针,迭代器可以用自定义类型实现

C++标准库提供了多种迭代器,不同类型的迭代器可以在不同的场景中使用:

1. 输入迭代器(单向,只读) (Input Iterator)
   - 允许只读访问元素。
   - 可以对序列进行一个方向的遍历。
   - 只能*解引用一次,每个元素只能读取一次。
   - 示例:std::istream_iterator

2. 输出迭代器(单向,只写) (Output Iterator)

   - 将数据写入序列中
   - 允许对元素的写操作。
   - 只能对序列进行一个方向的遍历。
   - 和输入迭代器一样,每个元素只能写入一次。
   - 示例:std::ostream_iterator

3. 前向迭代器(单向,可读可写,可以多次访问) (Forward Iterator)
   - 可以多次解引用。
   - 支持单向遍历。
   - 可以在迭代中保留状态,适合于需要多次访问同一个元素的场景。
   - 示例:std::forward_list,std::list

4. 反向迭代(单向,可读可写,可以多次访问)(Reverse Iterator)
   - 可以多次解引用。
   - 支持反向向遍历。
   - 可以在不改变容器本身的情况下,从容器的最后一个元素开始向前访问每个元素,直到访问到第一个元素
   -在 C++ STL 中,反向迭代器通常通过 rbegin() 和 rend() 方法获取

5. 双向迭代器(双向,可读可写,可以多次访问) (Bidirectional Iterator)
   - 可以在两个方向上遍历(即前向和后向)。
   - 支持多次解引用。
   - 没有减法,不能[]访问
   - 示例:std::list, std::set, std::map

6. 随机访问迭代器 (双向,可读可写,可以多次访问,支持随机访问)(Random Access Iterator)
   - 允许在常数时间内访问任意元素。
   - 支持所有双向迭代器的特性,并且可以进行加法、减法、比较等操作,可以[]访问。
   - 示例:std::vector, std::deque, std::array

3.string的使用

3.1 构造函数

void test1()
{
	string s1;//默认构造
	string s2(s1);//拷贝构造
	char s[] = "abc123";
	string s3(s);//对c风格字符串进行拷贝
	string s4(s3, 3, 2);//从s3的pos=3位置开始拷贝两个字符
	string s5(s3, 2);//从s3的pos=2位置开始拷贝直到s3结束
	string s6("abcdddd", 4);//对c风格字符串进行4个元素拷贝
	string s7('s', 3);//用3个‘s’初始化
	/
	//用迭代器初始化
	//函数模板,支持不同类的迭代器
	vector<char> v(10);
	char ch = 'a';
	for (auto& c : v)
	{
		c = ch++;
	}
	string s8(v.begin(), v.end());
}

size_t len = npos:
npos等于-1,size_t的-1赋值给len表示整型最大值,实际意思是用npos缺省时拷贝到底,有多少拷贝多少,len超出原有长度也是拷贝到底。

 3.2析构函数

 不需要自己调用

3.3operator=赋值运算符重载

void test2()
{
	string s1,s2;
	s1 = "123456";
	s2 = s1;
	cout << s1 << endl;
	cout << s2 << endl;
	s1 = 'a';
	s2 = "abcdef";
	cout << s1 << endl;
	cout << s2 << endl;
}

使用赋值运算符时,会对原有数据进行覆盖

要分清拷贝构造和赋值:

 

 3.4 begin()

返回字符串开头的迭代器
迭代器是随机访问迭代器
const_iterator begin() const用于const对象,返回的迭代器解引用无法进行修改
迭代器it++向后遍历

void test3()
{
	string s("abcdefg");
	std::string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		it++;
	}	
	cout << endl;
	it = s.begin();
	while (it != s.end())
	{
		*it += 2;
		cout << *it;
		it++;
	}
}
void test4()
{
	const string s("11111222233333");
	std::string::const_iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		it++;
	}
	cout << endl;
	//it = s.begin();
	//while (it != s.end())
	//{
	//	*it += 2;//it指向内容不可修改
	//	cout << *it;
	//	it++;
	//}
}

3.5end()

返回字符串最后一位有效数据的下一位的迭代器
迭代器是随机访问迭代器
const_iterator begin() const用于const对象,返回的迭代器解引用无法进行修改
如果对象是空字符串,begin()和end()返回相同的迭代器(迭代器支持比较)
begin()与end()的范围是左闭右开[)

3.6 rbegin() 和 rend()

返回最后一位有效数据的反向迭代器
返回反向迭代器以反向开始
反向迭代器++向字符串的开头遍历

void test5()
{
	string s("abcdefg");
	std::string::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	cout << endl;;
	rit = s.rbegin();
	while (rit != s.rend())
	{
		*rit += 3;
		cout << *rit;
		rit++;
	}
}
void test6()
{
	const string s("abcdefg");
	std::string::const_reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	//cout << endl;;
	//rit = s.rbegin();
	//while (rit != s.rend())
	//{
	//	*rit += 3;//const对象,const_reverse_iterator指向的内容不可以修改
	//	cout << *rit;
	//	rit++;
	//}
}

 3.7 rend()

将反向迭代器返回到反向结束
返回一个反向迭代器,该迭代器指向字符串第一个有效数据的前一个(作为反向结尾)
rbegin()和rend()表示范围:(]

3.8 cbegin() 和 cend(()

 与begin()和end()的区别:begin()和end()返回的迭代器可读可写,cbegin() 和 cend() 返回的迭代器是const_iterator只读不写,即便调用的对象本身是可读可写的。

3.9 crbegin() 和 crend()

 与rbegin()和rend()的区别:rbegin()和rend()返回的迭代器可读可写,crbegin() 和 crend() 返回的迭代器是const_reverse_iterator只读不写,即便调用的对象本身是可读可写的。

3.10 size()

 返回字符串的长度(不算'\0')

3.11 length()

返回字符串的长度(不算'\0')
跟size()作用是一样的

在 C++ 早期版本的标准库及其与 STL(标准模板库)的设计过程中,可能出于兼容性和可读性的考虑,保留了这两个方法。length() 方法是更早设计的,而 size() 方法也在其他 STL 容器中得到了广泛应用。
许多 STL 容器(如 std::vectorstd::deque 等)也提供了 size() 方法。因此,为了保持与其他容器的一致性,std::string 中也引入了 size() 方法。调用 size() 对于开发者来说,更符合 STL 的使用习惯。

3.12 max_size()

返回字符串可以达到的最大长度。
但实际上并不能达到最大长度,所以max_size()没什么用

 3.13 resize()

作用:将字符串的长度调整至n个长度;如果n小于原长,则会删除n长度以外的数据;如果n大于原长且没有指定字符c当初始值,则使用空字符('\0')来初始化,而且此时的长度是n;指定字符c当初始值则将原数据以外的位置全部给c初始值

void test10()
{
	string s("abcdefg");
	cout << s.size() << endl;
	s.resize(3);
	cout << s.size() << endl;
	cout << s << endl;
	s.resize(10);
	cout << s.size() << endl;
	cout << s << endl;
}

3.14 capacity()

 返回已分配容量的大小(单位:字节)

 长度size不一定等于容量大小capacity。

3.15 reserve()

请求将字符串容量调整为计划的大小更改,长度最多为n个字符
当n大于当前字符串容量时,扩容到n个大小
当n小于等于字符串容量时,这被认为是一个非强制性的请求,具体操作不同编译器下不同


在vs2022下:没办法通过reserve缩容(即使有些空间没有使用):

3.16 clear()

 清空字符串,将长度size置为0,不改变容量capacity

3.17 empty()

判断是否为空字符串,如果为空返回true,如果不为空返回false

3.18 shrink_to_fit()

请求字符串减少其容量以适合其大小。

 3.19 operator[]

返回pos位置的引用(const对象数据不可修改)
当pos等于size() 长度时,返回'\0'

普通数组[]访问越界可能没有报错,string[]访问越界直接报错(有assert进行越界检查)

 3.20 at()

返回pos位置的引用(const对象数据不可修改)
当pos等于size() 长度时,返回'\0'

at()与operator[] 的区别:底层处理越界不同,[]是暴力检查assert,at可以被捕获异常

3.21 back()

返回字符串最后一个字符的引用
空字符串无法调用

 3.22 front()

返回字符串第一个字符的引用
空字符串无法调用 

3.23 operator+=

在当前字符串后面追加字符串(或字符)并返回追加后的字符串的引用

3.24 append()

 在当前字符串后面追加字符串(或字符)并返回追加后的字符串的引用

void test20()
{
	string s("123456789");
	s.append(s);//s后面追加一个s
	s.append(s, 4,5);//从s的4位置开始9后面5个字符追加到s后面
	s.append("abc");//s后面追加c风格的字符串
	s.append("def", 2);//s后面追加c风格的前2个字符
	s.append(10,'u');//s后面追加10个字符'u'
	cout << s << endl;
	vector<char> v(10);
	for (int i = 0; i < 10; i++)
	{
		v[i] = 'a' + i;
	}
	s.append(v.begin(), v.end());//使用迭代器范围追加
	cout << s << endl;
}

 3.25 push_back()

尾插一个字符

3.26 assign()

为字符串分配一个新值,替换其当前内容 

3.27 insert()

指定原字符串的pos位置开始插入字符或字符串
重载函数返回iterator指向新插入字符的位置

3.28 erase()

删除字符串的一部分
重载函数返回iterator指向被删除部分的下一位

3.29 replace()

替换字符串的一部分

3.30 swap()(string成员函数)

交换两个对象的数据

3.31 pop_back()

 删除最后一个字符

3.32 c_str()

将对象转换成c风格的字符串,并返回其const修饰的指针,不可修饰指针指向的内容

 

3.33 data()

返回一个指向字符串内部字符数组的指针。这个指针可以用来直接访问字符串中的字符。

3.34 get_allocator()

用于获取与该容器关联的分配器(allocator)
返回一个分配器对象,这个对象用于在容器内部分配和释放内存
分配器是一个用于管理动态内存分配的对象,允许用户在使用 STL 容器时自定义内存管理行为

void test3()
{
	string s("abcdefghijklmnopq");
	// 获取与 s 关联的分配器
	std::allocator<char> alloc = s.get_allocator();
	//使用分配器分配内存--分配10个字符的内存
	char* p = alloc.allocate(10);
	//使用分配器释放内存
	alloc.deallocate(p, 10);
}

3.35 copy()

将字符串对象的当前值的子字符串复制到 s 指向的数组中。此子字符串包含从位置pos开始的len个字符
返回复制的字符个数
注意:函数不会在复制内容的末尾附加 '\0 '字符

3.36 find()

在字符串中正序搜索由其参数指定的序列第一次出现的字符或字符串,并返回第一个字符的pos

void test5()
{
	string s("abc11112345");
	size_t pos = s.find("123",0);
	cout << pos << endl; //6
	size_t pos1 = s.find("bcdef", 0, 2);//搜索"bcdef"的前两位"bc"
	cout << pos1 << endl; //1
	size_t pos2 = s.find('1');
	cout << pos2 << endl; //3
}

3.37 rfind()

在字符串中逆序搜索由其参数指定的序列最后一次出现的字符或字符串,并返回第一个字符的pos 

void test5()
{
	string s("abc123612345");
	size_t pos = s.rfind("123", s.size() - 1);
	cout << pos << endl; //7
	size_t pos1 = s.rfind("bcdef", s.size() - 1, 2);//搜索"bcdef"的前两位"bc"
	cout << pos1 << endl; //1
	size_t pos2 = s.rfind('1');
	cout << pos2 << endl; //7
}

3.38 find_first_of()

 在字符串中正序搜索与其参数中指定的序列中任何字符匹配(有一个字符匹配即可)的第一个字符,并返回其pos

void test6()
{
	string s("112334567");
	size_t pos = s.find_first_of("asdf2");
	cout << pos << endl; // 2
}

3.39 find_last_of()

在字符串中逆序搜索与其参数中指定的序列中任何字符匹配(有一个字符匹配即可)的第一个字符,并返回其pos

void test6()
{
	string s("1123345267");
	size_t pos = s.find_last_of("asdf2");
	cout << pos << endl; // 7
}

3.40 find_first_not_of()

 在字符串中正序搜索第一个字符,该字符与其参数中指定的任何字符都不匹配,并返回对应的pos

void test7()
{
	string s("123456665");
	size_t pos = s.find_first_not_of("asdf", 0);
	cout << pos << endl; //0
	size_t pos1 = s.find_first_not_of("12346", 0);
	cout << pos1 << endl; // 4
}

3.41 find_last_not_of()

在字符串中逆序搜索第一个字符,该字符与其参数中指定的任何字符都不匹配,并返回对应的pos 

void test7()
{
	string s("123456665");
	size_t pos = s.find_last_not_of("asdf", s.size() - 1);
	cout << pos << endl; // 8
	size_t pos1 = s.find_last_not_of("12346", s.size() - 1);
	cout << pos1 << endl; // 8
}

3.42 substr()

构造一个子字符串并返回

3.43 compare()

将字符串对象(或子字符串)的值与其参数指定的字符序列进行比较
>返回1, == 返回0, <返回-1
 

3.44 operator+(非成员函数)

构造一个新字符串,lhs的后面尾接rhs

3.45 各种运算符重载

3.46 swap()(非成员函数)

假设调用两个string对象调用上面的swap,swap会先拷贝构造一个对象,再进行交换,这样做代价太大,实际上只需要交换这两个对象的数据即可,所以标准库里重载了一个非成员函数专门给string使用,以防调用上面代价大的方式

这个swap会直接交换两个对象的数据

3.47 operator>> 和 operator<< (非成员函数)

 cin默认规定空格或换行是多个值之间的分割

 

3.48 getline() (非成员函数) 

getline可以自定义分隔符,遇到分割符才截至,默认遇到换行才截至,空格不截至,当自定义分割符时,换行不再充当分割符

4.string遍历

4.1 下标+[] 访问

void test12()
{
	string s = "abcdefgh";
	for (int i = 0; i < s.size(); i++)
	{
		s[i] += 1;
		cout << s[i] << ' ';
	}
}

4.2 迭代器访问(主流的通用访问方式)

void test13()
{
	string s = "abcdefgh";
	string::iterator it = s.begin();
	while (it != s.end())
	{
		(*it) += 1;
		cout << (*it) << ' ';
		it++;
	}
}

4.3 范围for(底层是用迭代器实现的)

void test14()
{
	string s = "abcdefgh";
	for (auto& c : s)
	{
		c += 1;
	}
	for (auto c : s)
	{
		cout << c << ' ';
	}
}

数组和所有容器都支持范围for,因为底层是由迭代器实现的,容器都可以通过迭代器遍历

5.const_iterator 和 iterator

void test15()
{
	string s("123456");
	string::iterator it = s.begin();
	//const_iterator指向的内容不能修改
	const string s1("abc");
	string::const_iterator it1 = s.begin();
	//const iterator指it2不能修改,it2指向的内容可以修改
	string s2("aaaa");
	const string::iterator it2 = s2.begin();
	*it2 = 'b';
	cout << s2 << endl;
}

6.sort() 算法

sort()函数是算法库里面模板函数,支持随机访问迭代器
sort()函数默认升序排序(<升序,>降序)
实现降序需要传递仿函数

void test16()
{
	string s("anfaskdnvqruqwoetufvvzx");
	sort(s.begin(), s.end());
	cout << s << endl;
	//gt仿函数是greater类型的对象,greter类型支持大小的比较(>)
	greater<char> gt;
	sort(s.begin(), s.end(), gt);
	cout << s << endl;
	//ls仿函数是less类型的对象,less类型支持大小的比较(<)
	less<char> ls;
	sort(s.begin(), s.end(), ls);
	cout << s << endl;
	//匿名对象写法
	sort(s.begin(), s.end(), greater<char>());
	cout << s << endl;
}

 

7.reverse()

用于反转范围内的元素

 

8.string的扩容

string如何扩容,c++标准没有规定,具体实现取决于编译器实现

vs2022下:

vs2022下,string默认有个16字节的_Buf数组,其中一个字节是留给'\0'的,剩下15字节作为容量,当所需容量小于15字节,string字符存在_Buf数组中;当所需空间大于15字节时,再动态开辟内存将所有字符存在开辟的内存中



Linux下:

9. shrink_to_fit() 的缩容

由于空间不能分段释放,所以缩容时会另开一块更小的空间,再将数据进行拷贝,释放原来的空间

10.to_string() 和 stoi

将数值转换成string并返回

 

string转换成整型并返回

11.string部分底层简单模拟实现

//string.h

#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <iostream>
#include <assert.h>

using namespace std;

namespace myString
{
	class string
	{
	public:
		//可以不用iterator直接用char*
		//把迭代器进行封装
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin();
		iterator end();
		const_iterator begin() const;
		const_iterator end() const;

		//string();
		//""默认有一个'\0'
		string(const char* str = "");
		string(const string& str);
		~string();
		const char* c_str() const;
		//string& operator=(const string& str);
		string& operator=(string str);

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

		void reserve(size_t n);

		void push_back(char ch);
		void append(const char* str);

		string& operator+= (char ch);
		string& operator+=(const char* str);

		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void earse(size_t pos, size_t len = npos);
		void swap(string& str);
		string substr(size_t pos = 0, size_t len = npos);

		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);

		bool operator<(const string& str)const;
		bool operator<=(const string& str)const;
		bool operator>(const string& str)const;
		bool operator>=(const string& str)const;
		bool operator==(const string& str)const;
		bool operator!=(const string& str)const;
		void clear(); 

	private:
		//char _buff[16];
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;

		//静态成员变量不能给缺省值,因为不走初始化列表
		//特例:static的const的整型可以给缺省值--既是声明也是定义
		const static size_t npos;
	};

	ostream& operator<<(ostream& os, const string& str);
	istream& operator>>(istream& is, string& str);
}
//string.cpp

#include "string.h"

//这样是因为多个文件同一个命名空间会合并
namespace myString
{
	//static 成员变量类内声明,类外定义
	//static成员变量属于静态区,若在string.h定义,在多文件展开就会冲突
	const size_t string::npos = -1;
	string::iterator string:: begin()
	{
		return _str;
	}
	string::iterator string::end()
	{
		return _str + _size;
	}
	string::const_iterator string::begin() const
	{
		return _str;
	}
	string::const_iterator string::end() const
	{
		return _str + _size; 
	}
	//错误写法
	//可能出现解引用nullptr的错误
	//string::string()
	//{
	//	_str = nullptr;
	//	_size = _capacity = 0;
	//}
	//strlen不算入'\0'
	//_size不算入'\0'
	//_capacity不算入'\0'
	string::string(const char* str)
		:_size(strlen(str))
	{
		//""会被strcpy拷贝过去
		//+1是因为'\0'
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, str);
	}
	传统写法
	//string::string(const string& str)
	//{
	//	_str = new char[str._capacity + 1];
	//	strcpy(_str, str._str);
	//	_size = str._size;
	//	_capacity = str._capacity;
	//}
	// 
	//现代写法(特点:让别人干活,再交换)
	string::string(const string& str)
	{
		//调用构造干活
		string tmp(str._str);
		swap(tmp);
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}
	const char* string::c_str() const
	{
		return _str;
	}
	传统写法
	//string& string::operator=(const string& str)
	//{
	//	if(this != &str)
	//	{
	//		char* tmp = new char[str._capacity + 1];
	//		strcpy(tmp, str._str);
	//		delete[] _str;
	//		_str = tmp;
	//		_size = str._size;
	//		_capacity = str._capacity;
	//	}
	//	return *this;
	//}
	现代写法
	//string& string::operator=(const string& str)
	//{
	//	if (this != &str)
	//	{
	//		string tmp(str._str);
	//		swap(tmp);
	//	}
	//	return *this;
	//}
	//现代写法再进化
	string& string::operator=(string str)
	{
		//str局部对象,出了作用域析构
		swap(str);
		return *this;
	}
	size_t string::size() const
	{
		return _size;
	}
	char& string::operator[] (size_t pos)
	{
		assert(pos < _size && pos >= 0);
		return _str[pos];
	}
	const char& string::operator[] (size_t pos)const
	{
		assert(pos < _size && pos >= 0);
		return _str[pos];
	}
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;

			_str = tmp;
			_capacity = n;
		}
	}
	void string::push_back(char ch)
	{
		if(_size == _capacity)
		{
			size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newCapacity);
		}
		_str[_size] = ch;
		_str[++_size] = '\0';
	}
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if(_size + len > _capacity)
		{
			reserve(_size + len);
		}
		//不用strcat('\0'问题,而且从头遍历效率不高),用strcpy就行
		strcpy(_str+_size, str);
		_size += len;
	}
	string& string::operator+= (char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	void string::insert(size_t pos, char ch)
	{
		assert(pos >= 0 && pos <= _size);
		if (_size == _capacity)
		{
			size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newCapacity);
		}
		//方法1:
		//int end = _size;
		//while (end >= (int)pos)//类型不同,发生隐式类型转换,有符号转换成无符号
		//{
		//	_str[end + 1] = _str[end];
		//	--end;
		//}
		//方法2:
		size_t end = _size + 1;
		while (end > pos)//类型不同,发生隐式类型转换,有符号转换成无符号
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = ch;
		++_size;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos >= 0 && pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		int end = _size;
		while (end >= (int)pos)
		{
			_str[end + len] = _str[end];
			--end;
		}
		memcpy(_str + pos, str, len);
		_size += len;
	}
	void string::earse(size_t pos, size_t len)
	{
		assert(pos >= 0 && pos < _size);

		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= len;
		}
	}
	void string::swap(string& str)
	{
		std::swap(_str, str._str);
		std::swap(_size, str._size);
		std::swap(_capacity, str._capacity);
	}
	string string::substr(size_t pos, size_t len)
	{
		if (len > _size - pos)
		{
			string sub(_str + pos);
			return sub;
		}
		else
		{
			string sub;
			sub.reserve(len);
			for (size_t i = 0; i < len; i++)
			{
				sub += _str[pos + i];
			}
		}
	}
	size_t string::find(char ch, size_t pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)
	{
		const char* p = strstr(_str + pos , str);
		return p - _str;
	}
	bool string::operator<(const string& str)const
	{
		return strcmp(_str, str._str) < 0;
	}
	bool string::operator<=(const string& str)const
	{
		return *this < str || *this == str;
	}
	bool string::operator>(const string& str)const
	{
		return strcmp(_str, str._str) > 0;
	}
	bool string::operator>=(const string& str)const
	{
		return !(*this < str);
	}
	bool string::operator==(const string& str)const
	{
		return strcmp(_str, str._str) == 0;
	}
	bool string::operator!=(const string& str)const
	{
		return !(*this == str);
	}
	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
	ostream& operator<<(ostream& os, const string& str)
	{
		for (size_t i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}
	缺点:频繁扩容
	//istream& operator>>(istream& is, string& str)
	//{
	//	str.clear();
	//	char ch = is.get();
	//	while (ch != ' ' && ch != '\n')
	//	{
	//		str += ch;
	//		ch = is.get();
	//	}
	//	return is;
	//}
	
	//cin优化方案--模拟缓冲区减少扩容的次数
	istream& operator>>(istream& is, string& str)
	{
		str.clear();
		char buff[128];
		int i = 0;
		char ch = is.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = is.get();
		}
		if (i != 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return is;
	}
}

12.实现过程中的一些问题

12.1 #include问题

#include "" 双引号默认在源文件当前目录下查找,找不到再去标准库里查找
#include <>是直接在标准库里查找
 

12.2 不同文件相同命名空间

多个文件同一个命名空间会合并

12.3 delete和free可以对nullptr

在C++中,delete 和 free 对 nullptr 的行为是安全的,可以安全地对 nullptr 调用,而不会造成任何问题。

12.4 实现范围for

范围for的底层就是迭代器,自己定义的类需要自己实现迭代器就能使用范围for(一定要实现begin()和end(), 且函数名字不能变,iterator可以变名字)

12.5 strcat()

strcat会从头开始遍历找'\0',覆盖找到的'\0'开始拼接,并在拼接后的字符串添加'\0'

12.6 静态成员变量

静态成员变量不能给缺省值,因为不走初始化列表
特例:static的const的整型可以给缺省值--既是声明也是定义
static 成员变量类内声明,类外定义
static 成员变量属于静态区,若在string.h定义,在多文件展开就会冲突

12.7 引用计数的写时拷贝(延时拷贝)

深拷贝需要重新开辟空间再进行赋值,浅拷贝只是将地址按字节拷贝,假设构造的这个对象不进行修改操作,可以直接用浅拷贝减少深拷贝的消耗。

浅拷贝的问题及其解决:
1.不同对象指向同一块资源,导致析构两次报错
解决方法:用引用计数,在析构函数内部对引用计数进行判断,使最后一个析构的对象释放空间。
2.一个修改影响另一个
解决方法:写时拷贝,需要修改的对象重新开辟并指向新的空间,然后进行拷贝并修改,不修改的对象仍然指向原来的空间

不修改就赚了,可以提升效率,linux下使用这个方案

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

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

相关文章

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第六十一章 Linux内核定时器

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

科普文:分布式数据一致性协议Paxos

1 什么是Paxos Paxos协议其实说的就是Paxos算法, Paxos算法是基于消息传递且具有高度容错特性的一致性算 法&#xff0c;是目前公认的解决分布式一致性问题最有效的算法之一。 Paxos由 莱斯利兰伯特(Leslie Lamport)于1998年在《The Part-Time Parliament》论文中首次公 开&…

KDP开源平台升级,推进大数据处理迈向轻量化、智能化

本文由 LeetTools 工具生成 编辑 | June 在当今数字化转型的浪潮中&#xff0c;企业面临着如何高效管理和利用大数据的挑战。智领云推出的Kubernetes Data Platform&#xff08;简称KDP&#xff09;正是为了解决这一问题而设计的。作为一款开源的云原生大数据平台&#xff0c;K…

【前端 08】简单学习js字符串

JavaScript中的String对象详解 在JavaScript中&#xff0c;字符串&#xff08;String&#xff09;是一种非常基础且常用的数据类型&#xff0c;用于表示文本数据。虽然JavaScript中的字符串是原始数据类型&#xff0c;但它们的行为类似于对象&#xff0c;因为JavaScript为字符…

[C#]调用本地摄像头录制视频并保存

AForge.NET是一个基于C#框架设计的开源计算机视觉和人工智能库&#xff0c;专为开发者和研究者设计。它提供了丰富的图像处理和视频处理算法、机器学习和神经网络模型&#xff0c;具有高效、易用、稳定等特点。AForge库由多个组件模块组成&#xff0c;包括AForge.Imaging&#…

算法日记day 19(找树左下角的值|路径总和)

一、找树左下角的值 题目&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7 思路&#xff1a;…

【多线程】定时器

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 定时器是什么&#xff1f;2. 定时器的应用场景3. Timer类的使用3.1 Timer类创建定时器3.2 schedule()方法…

Unity横板动作游戏 -项目准备

项目准备 这是一篇 Unity 2022 最新稳定版本的教程同步笔记&#xff0c;本文将会讲解一些开始学习必须的条件。 安装环境 首先是安装 UnityHub&#xff0c;然后在 UnityHub 中安装 Unity 的版本(2022)。 只需要安装 开发者工具 和文档即可&#xff0c;导出到其他平台的工具等…

学习Vue2收藏这一篇就够了(如何创建Vue实例)

什么是Vue&#xff1f; Vue是什么&#xff1a;是一个用于构建用户界面的渐进式框架 什么是构建用户界面&#xff1a;基于数据动态渲染页面 什么是渐进式&#xff1a;循序渐进的学习 什么是框架&#xff1a;一整套完整的项目解决方案 创建Vue实例 核心步骤&#xff08;4步…

《javaEE篇》--单例模式详解

目录 单例模式 饿汉模式 懒汉模式 懒汉模式(优化) 指令重排序 总结 单例模式 单例模式属于一种设计模式&#xff0c;设计模式就好比是一种固定代码套路类似于棋谱&#xff0c;是由前人总结并且记录下来我们可以直接使用的代码设计思路。 单例模式就是&#xff0c;在有…

音视频入门基础:WAV专题(3)——FFmpeg源码中,判断某文件是否为WAV音频文件的实现

一、引言 通过FFmpeg命令&#xff1a; ./ffmpeg -i XXX.wav 可以判断出某个文件是否为WAV格式的音频文件&#xff1a; 所以FFmpeg是怎样判断出某个文件是否为WAV格式的音频文件呢&#xff1f;它内部其实是通过wav_probe函数来判断的。从文章《FFmpeg源码&#xff1a;av_prob…

02、爬虫数据解析-Re解析

数据解析的目的是不拿到页面的全部内容&#xff0c;只拿到部分我们想要的内容内容。 Re解析就是正则解析&#xff0c;效率高准确性高。学习本节内容前需要学会基础的正则表达式。 一、正则匹配规则 1、常用元字符 . 匹配除换行符以外的字符 \w 匹配字母或数字或下划…

软件测试---网络基础、HTTP

一、网络基础 &#xff08;1&#xff09;Web和网络知识 网络基础TCP/IP 使用HTTP协议访问Web WWW万维网的诞生 WWW万维网的构成 &#xff08;2&#xff09;IP协议 &#xff08;3&#xff09;可靠传输的TCP和三次握手策略 &#xff08;4&#xff09;域名解析服务DNS &#xff0…

一篇文章学完Python基础

1. 字符串 str1 "Hello" str2 " World" print(str1 str2) # 输出&#xff1a;HelloWorld 1.1 字符替换 text "Hello, World!" new_text text.replace("World", "Python") print(new_text) # 输出&#xff1a;…

大数据-52 Kafka 基础概念和基本架构 核心API介绍 应用场景等

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

橙单前端项目下载编译遇到的问题与解决

今天下载orange-admin前端项目&#xff0c;不过下载下来运行也出现一些问题。 1、运行出现下面一堆错误&#xff0c;如下&#xff1a; 2、对于下面这个错误 error Expected linebreaks to be LF but found CRLF linebreak-style 这就是eslint的报错了&#xff0c;可能是原作者…

全开源收银系统源码-支付通道

1.收银系统开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后合管理网站: HTML5vue2.0element-uicssjs线下收银台&#xff08;安卓/PC收银、安卓自助收银&#xff09;: Dart3框架&#xff1a;Flutter 3.19.6助手: uniapp商城: uniapp 2.支付通道 智慧新零售收银系统…

SQL语句(以MySQL为例)——单表、多表查询

笛卡尔积&#xff08;或交叉连接&#xff09;: 笛卡尔乘积是一个数学运算。假设我有两个集合 X 和 Y&#xff0c;那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合&#xff0c;也就是第一个对象来自于 X&#xff0c;第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中…

天机学堂第二天项目 添加我的课表 项目总结

目录 根据产品原型得到数据库表结构 RabbitMq监听 构造器注入 幂等 mybatisplus 分页查询的多种写法 在new page里面添加排序 查询条件中 用orderBydESC指定排序 ​编辑 链式编程中使用page指定排序 stream流 ​编辑 在网关中解析token 根据产品原型得到数据库表结构 根…

IDEA Maven使用HTTP代理,解决Could not transfer artifact org.xxx问题

文章目录 一、前言二、遇到问题三、分析问题四、HTTP代理五、重新编译验证 一、前言 遇到这个问题&#xff0c;有两种解决办法 IDEA Maven使用HTTP代理&#xff0c;解决Could not transfer artifact org.xxx问题IDEA Maven使用国内镜像&#xff0c;解决Could not transfer arti…