当前位置: 首页 > news >正文

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){}*/// 短小频繁调用的函数,可以直接定义到类里面,默认是inlinestring(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;}}//找不到返回-1return 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大于剩余字符长度,更新一下lenif (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;}
};
387. 字符串中的第一个唯一字符 - 力扣(LeetCode)
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);elseReverse(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;}elsesign = 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;}elsesign = 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的每一个数据乘以num1MulItem(tmp, num1, num2[i]);//将乘得的结果进行错位相加MoveAdd(result, tmp, i);tmp.clear();}//处理前导0while (result.size() != 1 && result.back() == '0')result.pop_back();//翻转数据,恢复数据reverse(result.begin(), result.end());return result;}
};

 

 

 

http://www.xdnf.cn/news/1247509.html

相关文章:

  • NLP自然语言处理 02 RNN及其变体
  • GPS信号捕获尝试(上)
  • 基于 Ubuntu 的 Linux 系统中 Vivado 2020.1 下载安装教程
  • Modbus tcp 批量写线圈状态
  • 【STM32】HAL库中的实现(四):RTC (实时时钟)
  • ES 模块动态导入
  • BeanFactory 和 ApplicationContext 的区别?
  • centos通过DockerCompose搭建开源MediaCMS
  • 如何让 RAG 检索更高效?——大模型召回策略全解
  • 字符串匹配--KMP算法
  • Arxiv-Daily
  • 【机器学习】算法调参的两种方式:网格搜索(枚举)、随机搜索
  • Spring AI 系列之三十六 - Spring AI Alibaba-nl2sql
  • 【Git学习】入门与基础
  • 调试|谷歌浏览器调试长连接|调试SSE和websocket
  • SELinux加固Linux安全
  • python的高校班级管理系统
  • 技术部实习总结
  • 暑期算法训练.14
  • Rust进阶-part3-生命周期
  • Docker Desktop
  • K8s Master状态NotReady
  • 组织架构与软件架构协同演进实践指南
  • 网络 —— 笔记本(主机)、主机虚拟机(Windows、Ubuntu)、手机(笔记本热点),三者进行相互ping通
  • Redis面试精讲 Day 11:Redis主从复制原理与实践
  • 微服务—Gateway
  • Solidity智能合约基础
  • python学智能算法(三十三)|SVM-构建软边界拉格朗日方程
  • 《零基础入门AI:传统机器学习进阶(从拟合概念到K-Means算法)》
  • 机器学习——集成学习(Ensemble Learning)详解:原理、方法与实战应用