C++ string类

C++ string类全面解析

目录

一.STL简介

1. 什么是STL

2.STL的版本(了解即可)

​编辑

二.String类 

1.认识string类

2.string类的常用接口说明

2.1构造函数

2.2 string类对象的容量操作

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

 2.5. string类非成员函数

2.6 vs和g++下string结构的说明

2.6.1 vs下string的结构

2.6.2 g++下string的结构

3.C++11的小语法(auto关键字和范围for)

3.1auto关键字(做类型推导)

3.2范围for(遍历方便)

4.string类的实现

string.h

string.cpp

test.cpp

5.浅拷贝和深拷贝问题

5.1浅拷贝

5.1深拷贝

6.string 在线OJ题目

一.STL简介

1. 什么是STL

        STL(standard template libaray-标准模板库 ) C++ 标准库的重要组成部分 ,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架

2.STL的版本(了解即可)

1.HP STL (原始版本 - 1994年):

  • 地位: 由Alexander Stepanov和Meng Lee在惠普实验室开发,是STL的原始实现和概念起源

  • 特点: 开源、模板化、包含核心组件(容器、算法、迭代器、函数对象)。奠定了STL的设计理念和基本架构。

  • 现状: 是历史起点,但已不再是主流使用版本。

2.SGI STL (标准化前的重要实现 - 1990年代末):

  • 地位: 由Silicon Graphics公司基于HP STL进行大幅改进和扩展的实现。

  • 特点:

    • 性能优化: 做了大量性能优化,效率很高。

    • 扩展组件: 引入了重要的非标准但广泛使用的组件,如 hash_maphash_set (后来成为标准的 unordered_mapunordered_set)、slist (单链表)、rope (长字符串处理) 以及强大的内存分配器(如 alloc

    • 文档优秀: 提供了非常清晰、深入的在线文档,对理解STL内部机制和设计思想影响深远。

  • 影响: 成为许多早期编译器(如GCC早期版本)STL实现的基础,其设计和扩展组件对C++标准库的发展产生了巨大影响。其文档至今仍是经典学习资料。

  • 现状: 本身作为一个独立实现已较少直接使用,但其思想和组件被广泛吸收。 

3.STLport (跨平台移植版本 - 1990年代末至2000年代初):

  • 地位: 一个旨在提供高性能、跨平台且符合标准的STL实现的开源项目。

  • 目标: 解决早期不同编译器(尤其是Windows上的VC++)自带STL实现质量、性能、可移植性和标准符合性不一致的问题。

  • 特点: 基于SGI STL,注重可移植性、调试模式支持、标准符合性(努力跟进C++98/03标准)。

  • 现状: 在早期VC++(如VC6)时代非常流行,作为替换编译器自带STL的方案。随着主流编译器(GCC, Clang, MSVC)自身STL实现质量的飞速提升和标准化,STLport的重要性已大大降低,项目基本停止活跃。

4.标准库STL实现 (主流编译器自带):

  • 地位: 目前绝对主流的STL使用方式。C++标准(C++98/03, C++11, C++14, C++17, C++20, C++23)定义了STL(严格来说是标准库中的容器、算法、迭代器、函数对象等部分)的接口和行为规范。

  • 实现者: 各大编译器厂商(或开源社区)负责根据标准规范实现自己的版本:

    • GNU (GCC): libstdc++ (通常称为GNU C++ Library)

    • LLVM/Clang: libc++

    • Microsoft (MSVC): MSVC STL (历史上曾称Dinkumware STL, 现在官方称MSVC STL)

    • 其他: Embarcadero C++ Builder, Intel C++ Compiler等也有其实现或适配。

  • 特点:

    • 标准符合性: 严格遵循(或努力追赶)最新的C++标准规范。

    • 与编译器深度集成: 紧密配合编译器的特性(如ABI、优化、错误信息)。

    • 持续演进: 不断加入对新标准特性(C++11/14/17/20/23)的支持(如移动语义、智能指针、新容器 unordered_map/setarrayforward_list, 并行算法、范围操作等)。

    • 性能优化: 各实现都在性能上进行激烈竞争和持续优化。

    • 平台适配性: 针对特定操作系统和硬件架构进行优化。

  • 现状: 是当今C++开发者直接使用的STL实现。选择哪个版本通常取决于你选择的编译器。

总结:

  • HP STL: 始祖,奠定基础。

  • SGI STL: 承前启后,性能卓越,扩展丰富,影响深远(特别是文档)。

  • STLport: 特定历史时期(跨平台/标准符合性需求强烈,编译器实现不佳时)的流行移植方案。

  • 编译器自带标准库STL (libstdc++libc++, MSVC STL): 绝对主流,严格遵循标准,深度集成编译器,持续演进更新。开发者日常接触和使用的就是这些实现。

主要包括这些内容: 

二.string类 

        C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列 的库函数,但是这些库函数与字符串是分离开的,底层空间需要用户自己管理,稍不留神可能还会越界访问。在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。 

1.认识string类

有关string类的介绍

注意:在使用string类时,必须包含#include头文件以及using namespace std;

2.string类的常用接口说明

2.1构造函数

文档链接:cplusplus.com/reference/string/string/string/

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1;
	string s2("hello world");
	string s3(s2);

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;

	//cin >> s1;
	//cout << s1 << endl;

	string s4(s2, 6, 15);
	cout << s4 << endl;

	string s5(s2, 6);
	cout << s5 << endl;

	string s6("hello world", 5);
	cout << s6 << endl;

	string s7(10, 'X');
	cout << s7 << endl;

	s6[0] = 'x';
	cout << s6 << endl;

	return 0;
}

2.2 string类对象的容量操作

文档链接:string - C++ Reference

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

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

迭代器(类似于指针):

正向迭代器:

反向迭代器:

#include<iostream>
#include<string>
#include<map>
#include<list>
using namespace std;


void test_string1()
{
	string s1;
	string s2("hello world");

	cout << s1 << s2 << endl;

	s2[0] = 'x';
	cout << s1 << s2 << endl;

	// 1、下标 + []
	for (size_t i = 0; i < s2.size(); i++)
	{
		cout << s2[i] << " ";
	}
	cout << endl;

	// 2、迭代器
	//string::iterator it = s2.begin();
	auto it = s2.begin();
	while (it != s2.end())
	{
		*it += 2;

		cout << *it << " ";
		++it;
	}
	cout << endl;

	cout << s2 << endl;

	map<string, string> dict;
	//map<string, string>::iterator mit = dict.begin();
	auto mit = dict.begin();

	/*list<int> lt = { 1,2,3,4,5,6,7 };
	list<int>::iterator lit = lt.begin();
	while (lit != lt.end())
	{
		cout << *lit << " ";
		++lit;
	}
	cout << endl;*/

	// 3、字符赋值,自动迭代,自动判断结束
	// 底层就是迭代器
	//for (auto ch : s2)
	for (auto& ch : s2)
	{
		ch -= 2;
		cout << ch << " ";
	}
	cout << endl;

	cout << s2 << endl;
}

void test_string2()
{
	string s2("hello world");
	string::iterator it = s2.begin();
	while (it != s2.end())
	{
		*it += 2;
		cout << *it << " ";
		++it;
	}
	cout << endl;

	string::reverse_iterator rit = s2.rbegin();
	while (rit != s2.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	const string s3("hello world");
	//string::const_iterator cit = s3.begin();
	auto cit = s3.begin();
	while (cit != s3.end())
	{
		//*cit += 2;
		cout << *cit << " ";
		++cit;
	}
	cout << endl;

	//string::const_reverse_iterator rcit = s3.rbegin();
	auto rcit = s3.rbegin();
	while (rcit != s3.rend())
	{
		// *rcit += 2;
		cout << *rcit << " ";
		++rcit;
	}
	cout << endl;
}

void TestPushBack()
{
	// reverse 反转  逆置
	// reserve 保留、预留
	string s;
	// 提前开空间,避免扩容,提高效率
	s.reserve(100);
	size_t sz = s.capacity();
	cout << "capacity changed: " << sz << '\n';

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

void test_string3()
{
	string s2("hello world");
	cout << s2.length() << endl;
	cout << s2.size() << endl;

	cout << s2.max_size() << endl;

	cout << s2.capacity() << endl;
	TestPushBack();

	string s3("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	//cout << sizeof(s2) << endl;
	//cout << sizeof(s3) << endl;
}
//reserve capacity 测试 不同的编译器可能不一样
void test_string4()
{
	string s2("hello worldxxxxxxxxxxxxx");
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	s2.reserve(20);
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	s2.reserve(28);
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	s2.reserve(40);
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	s2.clear();
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	cout << typeid(string::iterator).name() << endl;
	cout << typeid(string::reverse_iterator).name() << endl;

}

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

 2.4string类对象的修改操作 

注意:
1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差
不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可
以连接字符串。
2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留
好。

 2.5. string类非成员函数

 以上的接口介绍的没介绍的都可以自行查阅文档学习,不一一列举。

C library - C++ Reference

2.6 vs和g++下string结构的说明

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
2.6.1 vs下string的结构
string总共占 28个字节 ,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string中字符串的存储空间
当字符串长度小于16时,使用内部固定的字符数组来存放。
当字符串长度大于等于16时,从堆上开辟空间。
class string
{
private:
	char _buff[16];
	char*  _str;

	size_t _size;
	size_t _capacity;
};
union _Bxty
{ // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];
pointer _Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建
好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t 字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的
容量。
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。

2.6.2 g++string的结构
G++下,string是通过写时拷贝实现的,string对象总共占 4个字节 ,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段: 空间总大小   字符串有效长度 引用计数,指向堆空间的指针,用来存储字符串。
struct _Rep_base
{
_length;
size_type _M_capacity;
_Atomic_word _M_refcount;
};

关于写时拷贝和引用计数,有兴趣的可以阅读一下这篇大佬写的文章:

C++ STL string的Copy-On-Write技术 | 酷 壳 - CoolShell

写时拷贝 就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
引用计数 :用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该
资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有
其他对象在使用该资源。

3.C++11的小语法(auto关键字和范围for)

3.1auto关键字(做类型推导)

1.在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个
不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型
指示符,而是作为一个新的类型指示符来指示编译器, auto声明的变量必须由编译器在编译时期
推导而得
2.用auto声明指针类型时,用auto和auto*没有任何区别,但用 auto声明引用类型时则必须加&
3.当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
4. auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
5. auto不能直接用来声明数组
#include<iostream>
using namespace std;
int func1()
{
	return 10;
}
// 不能做参数
void func2(auto a)
{}

// 可以做返回值,但是建议谨慎使用
auto func3()
{
	return 3;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = func1();
	// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
	auto e;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	int x = 10;
	auto y = &x;
	auto* z = &x;
	auto& m = x;
	cout << typeid(x).name() << endl;
	cout << typeid(y).name() << endl;
	cout << typeid(z).name() << endl;
	auto aa = 1, bb = 2;
	// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
	auto cc = 3, dd = 4.0;
	// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
	auto array[] = { 4, 5, 6 };
	return 0;
}
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
	std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange",
	"橙子" }, {"pear","梨"} };
	// auto的用武之地 简单方便快捷
	//std::map<std::string, std::string>::iterator it = dict.begin();
	auto it = dict.begin();
	while (it != dict.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	return 0;
}

3.2范围for(遍历方便)

1.对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的 for 循环。 for 循环后的括号由冒号 分为两部分:第一部分是范围
内用于迭代的变量,第二部分则表示被迭代的范围 ,自动迭代,自动取数据,自动判断结束。
2. 范围for可以作用到数组和容器对象上进行遍历
3.范围 for 的底层很简单,容器遍历实际就是替换为 迭代器 ,这个从汇编层也可以看到。
#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	// C++98的遍历
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		array[i] *= 2;
	}
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		cout << array[i] << endl;
	}
	// C++11的遍历
	for (auto& e : array)
		e *= 2;
	for (auto e : array)
		cout << e << " " << endl;
	string str("hello world");
	for (auto ch : str)
	{
		cout << ch << " ";
	}
	cout << endl;
	return 0;
}

4.string类的实现

string.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

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

namespace sy
{
	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()
			:_str(new char[1]{'\0'})
			,_size(0)
			,_capacity(0)
		{}*/

		// 短小频繁调用的函数,可以直接定义到类里面,默认是inline
		string(const char* str = "")
		{
			_size = strlen(str);
			// _capacity不包含\0
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		// 深拷贝问题
		// s2(s1)
		/*string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}*/

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

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

		// s2 = s1
		// s1 = s1
		/*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;
		}*/

		// s1 = s3;
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		//string tmp(s._str);
		//		string tmp(s);

		//		swap(tmp);
		//	}

		//	return *this;
		//}

		// s1 = s3;
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

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

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

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

		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}

		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 copy_on_write()
		{
			if (count > 1)
			{
				深拷贝
			}
		}*/

		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 erase(size_t pos, size_t len = npos);

		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		string substr(size_t pos = 0, size_t len = npos);
	private:
		//char _buff[16];
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;

		//static const size_t npos = -1;
		static const size_t npos;

		/*static const int N = 10;
		int buff[N];*/
	};

	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& out, const string& s);
	istream& operator>>(istream& in, string& s);
}
string.cpp
#include"string.h"

namespace sy
{
	//静态成员变量⼀定要在类外进⾏初始化
	const size_t string::npos = -1;

	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)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}

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

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

	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+=(const char* str)
	{
		append(str);
		return *this;
	}

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

		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 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* s)
	{
		assert(pos <= _size);

		size_t len = strlen(s);
		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] = s[i];
		}

		_size += 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;
		}
	}

	size_t string::find(char ch, size_t pos)
	{
		assert(pos < _size);

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

		//找不到返回-1
		return npos;
	}

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

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

	string string::substr(size_t pos, size_t len)
	{
		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;
	}

	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 strcmp(s1.c_str(), s2.c_str()) == 0;
	}

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

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

		return out;
	}

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

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

		char ch;
		//in >> ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;

				i = 0;
			}

			//in >> ch;
			ch = in.get();
		}

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

		return in;
	}
}
test.cpp
#include"string.h"

namespace sy
{
	void test_string1()
	{
		string s1;
		string s2("hello world");
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;

		for (size_t i = 0; i < s2.size(); i++)
		{
			s2[i] += 2;
		}

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

		for (auto e : s2)
		{
			cout << e << " ";
		}
		cout << endl;

		string::iterator it = s2.begin();
		while (it != s2.end())
		{
			//*it += 2;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test_string2()
	{
		string s1("hello world");
		s1 += 'x';
		s1 += '#';
		cout << s1.c_str() << endl;

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

		s1.insert(5, '$');
		cout << s1.c_str() << endl;

		s1.insert(0, '$');
		cout << s1.c_str() << endl;

		string s2("hello world");
		cout << s2.c_str() << endl;

		s2.insert(5, "$$$");
		cout << s2.c_str() << endl;

		s2.insert(0, "$$$&&&&&&&&&&&&&&&&&&&&&&&&");
		cout << s2.c_str() << endl;
	}

	void test_string3()
	{
		string s1("hello world");
		s1.erase(6, 100);
		cout << s1.c_str() << endl;

		string s2("hello world");
		s2.erase(6);
		cout << s2.c_str() << endl;

		string s3("hello world");
		s3.erase(6, 3);
		cout << s3.c_str() << endl;
	}

	void test_string4()
	{
		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;

		s = suffix;
		cout << suffix.c_str() << endl;
		cout << s.c_str() << endl;

		s = s;
		cout << s.c_str() << endl;
	}

	void test_string5()
	{
		string s1("hello world");
		string s2("hello world");

		cout << (s1 < s2) << endl;
		cout << (s1 == s2) << endl;
		cout << ("hello world" < s2) << endl;
		cout << (s1 == "hello world") << endl;
		cout << ("hello world" == "hello world") << endl;

		cout << s1 << s2 << endl;

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

	void test_string6()
	{
		string s1("hello world");
		string s2 = s1;

		cout << s1 << endl;
		cout << s2 << endl;

		string s3("xxxxxxxxxxxxxx");
		s1 = s3;

		cout << s1 << endl;
		cout << s3 << endl;
	}

	void test_string7()
	{
		string s1("hello world");
		string s2("xxxxxxxxxxxxxxxxxxxxxxxx");

		std::swap(s1, s2);
		s1.swap(s2);
	}
}
int main()
{
	sy::test_string1();
	sy::test_string2();
	sy::test_string3();
	sy::test_string4();
	sy::test_string5();
	sy::test_string6();
	sy::test_string7();
	return 0;
}

5.浅拷贝和深拷贝问题

5.1浅拷贝

        浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
说明:上述String类(自己实现的) 没有 显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为 浅拷贝。 可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

5.1深拷贝

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

6.string 在线OJ题目

415. 字符串相加 - 力扣(LeetCode)

class Solution {
public:
    string addStrings(string num1, string num2) 
    {
        string str;
        int end1=num1.size()-1,end2=num2.size()-1;
        int next=0;
        while(end1>=0 || end2>=0)
        {
            int val1 = end1 >=0 ? num1[end1--]-'0' : 0;
            int val2 = end2 >=0 ? num2[end2--]-'0' : 0;

            int ret=val1+val2+next;
            next=ret / 10;
            ret=ret % 10;

            str +=('0'+ret);
        }

        if(next==1)

        str+='1';
        reverse(str.begin(),str.end());
        return str;
    }
};

917. 仅仅反转字母 - 力扣(LeetCode)

class Solution {
public:
        bool isLetter(char ch)
        {
        if(ch >= 'a' && ch <= 'z')
            return true;
        if(ch >= 'A' && ch <= 'Z')
            return true;
        return false;
        }
        string reverseOnlyLetters(string S) 
        {
            if(S.empty())
            return S;
            size_t begin = 0, end = S.size()-1;
            while(begin < end)
            {
                while(begin < end && !isLetter(S[begin]))
                    ++begin;
                while(begin < end && !isLetter(S[end]))
                    --end;
                swap(S[begin], S[end]);
                ++begin;
                --end;
            }
            return S;
        }
};
class Solution {

public:

//1. 分别对字符串进行正序查询和反序查询

// 2. 如果所查询的字符下标相等,说明此字符只出现了一次
  int firstUniqChar(string s) 
  {
    for(int i=0; i<s.size(); ++i)
    {
      int index = s.find(s[i]);
      int reverse_index = s.rfind(s[i]);  
      if(index == reverse_index)
        return i;
    }
    return -1;
  }

};
class Solution {
public:
    int firstUniqChar(string s) 
    {
        int a[256]={0};
        for(int i=0;i<s.size();i++)
        {
            a[s[i]]+=1;
        } 
        for(int i=0;i<s.size();i++)
        {
            if(1==a[s[i]])
            {
                return i;
            }
        }
        return -1;
    }
};

字符串最后一个单词的长度_牛客题霸_牛客网

#include <iostream>
#include <string>
#include <algorithm>

using namespace std;
int main()
{
    string str;

    getline(cin, str);
    reverse(str.begin(), str.end());
    string::iterator it = str.begin();
    int count = 0;
    while (it != str.end() && * it != ' ')
    {
        count++;
        ++it;
    }
    cout << count << endl;


    return 0;
}
#include<iostream>
#include<string>
using namespace std;
int main()
{
    string line;
    // 不要使用cin>>line,因为会它遇到空格就结束了
    // while(cin>>line)
    while(getline(cin, line))
    {
        size_t pos = line.rfind(' ');
        cout<<line.size()-pos-1<<endl;
    }
    return 0;
}
class Solution {
public:
    bool isletter(char ch)
    {
        if((ch >= 'a' && ch <= 'z') || 
        (ch >='A' && ch <='Z') ||
         (ch >='0' && ch <= '9'))
        {
            return true;
        }
        return false;
    }
    bool isPalindrome(string s) 
    {
        //全部转成小写字母
        for(auto& ch :s)
        {
            if(ch >='A' && ch <='Z')
            {
                ch+=32;
            }
        }

        int begin=0;
        int end=s.size()-1;
        while(begin < end)
        {
            while(begin < end && !isletter(s[begin]))
            {
                ++begin;
            }
            while(begin < end && !isletter(s[end]))
            {
                --end;
            }

            if(s[begin]!=s[end])
            {
                return false;
            }
            else
            {
                ++begin;
                --end;
            }

        }
        return true;
    }
};

541. 反转字符串 II - 力扣(LeetCode)

class Solution {
public:
    void Reverse(string& s, int start, int end) 
    {
        char tmp;
        end--;
        while (start < end)
        {
            tmp = s[start];
            s[start] = s[end];
            s[end] = tmp;
            start++;
            end--;
        }
    }
    string reverseStr(string s, int k)
    {
        int len = s.size();
        for (int i = 0; i < len; i += 2 * k)
        {
            if (i + k < len)
                Reverse(s, i, i + k);
            else
                Reverse(s, i, len);
        }
        return s;
    }
};

557. 反转字符串中的单词 III - 力扣(LeetCode)

class Solution {
public:
    string reverseWords(string s) 
    {
        string::iterator it = s.begin();
        int count = 0;
        int end = 0;
        auto ptr = s.begin();
        while (it != s.end())
        {
            if(*it == ' ')
            {
                std::reverse(ptr, s.begin() + count);
                ptr = s.begin() + count + 1;
            }
            count++;
            it++;
        }
        std::reverse(ptr, s.end());
        return s;

    }
};

43. 字符串相乘 - 力扣(LeetCode)

class Solution {
public:
void MulItem(string& tmp, string& num1, char a)
    {
        int i = 0, sign = 0;
        int mul = 0;
        while (i < num1.size())
        {
            mul = (num1[i] - '0') * (a - '0') + sign;
            if (mul >= 10)
            {
                sign = mul / 10;
                mul %= 10;
            }
            else
                sign = 0;
            tmp.push_back(mul + '0');
            i++;
        }
        if (sign > 0)
            tmp.push_back(sign + '0');
    }
    //对应位相加,sign进位采用引用传递
    int AddItem(int a, int b, int& sign)
    {
        int add = a + b + sign;
        if (add >= 10)
        {
            sign = 1;
            add -= 10;
        }
        else
            sign = 0;
        return add;
    }

    //错位相加
    void MoveAdd(string& result, string& tmp, int k)
    {
        int i, j;
        i = k;
        j = 0;
        int sign = 0;
        while (i < result.size() && j < tmp.size())
        {
            result[i] = AddItem(result[i] - '0', tmp[j] - '0', sign) + '0';
            i++;
            j++;
        }
        while (i < result.size() && sign)
        {
            result[i] = AddItem(result[i] - '0', 0, sign) + '0';
            i++;
        }
        while (j < tmp.size())
        {
            int v = AddItem(0, tmp[j] - '0', sign);
            result.push_back(v + '0');
            j++;
        }
        if (sign)
            result.push_back(sign + '0');
    }
    string multiply(string num1, string num2)

    {

        //先翻转数据,方便进位处理
        reverse(num1.begin(), num1.end());
        reverse(num2.begin(), num2.end());
        string tmp, result;

        for (int i = 0; i < num2.size(); ++i)
        {
            //使用num2的每一个数据乘以num1
            MulItem(tmp, num1, num2[i]);
            //将乘得的结果进行错位相加
            MoveAdd(result, tmp, i);
            tmp.clear();
        }
        //处理前导0
        while (result.size() != 1 && result.back() == '0')
            result.pop_back();
        //翻转数据,恢复数据
        reverse(result.begin(), result.end());
        return result;
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值