【C++中的STL(未完成)】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、pandas是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.读入数据
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、【STL简介]

网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构
以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

1.1【 什么是STL】


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

1.2【 STL的版本】


原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意
运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使
用。 HP 版本--所有STL实现版本的始祖。
P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,
符号命名比较怪异。
RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,
可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码,
主要参考的就是这个版本。

1.3【STL的六大组件】

1.4【STL的缺陷】


1. STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。
2. STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
3. STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
4. STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的。

二、【string】

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

这里附一份优质博客以供参考:

C++面试中string类的一种正确写法 | 酷 壳 - CoolShell

2.1 【string类】

1. 字符串是表示字符序列的类
2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
单字节字符字符串的设计特性。
3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
息,请参阅basic_string)。
4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个
类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
string;
4. 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;

2.2 【string类的常用接口说明】

1.【 string类对象的常见构造】

参考资料:

 http://www.cplusplus.com/reference/string/string/string/

using namespace std;
void TestString1()
{string s1;string s2("hello world");string s3(s2);cout << &s1 << endl;cout << s1 << endl;cout << &s2 << endl;cout << s2 << endl;cout << &s3 << endl;cout << s3 << endl;
}int main()
{TestString1();return 0;
}

2. 【string类对象的容量操作】

参考资料:

size:http://www.cplusplus.com/reference/string/string/size/

length:http://www.cplusplus.com/reference/string/string/length/

capacity:http://www.cplusplus.com/reference/string/string/capacity/

empty:http://www.cplusplus.com/reference/string/string/empty/

clear:http://www.cplusplus.com/reference/string/string/clear/

reserve:http://www.cplusplus.com/reference/string/string/reserve/

resize:http://www.cplusplus.com/reference/string/string/resize/

string ::size和string::length都是同义词并返回相同的值

void TestString2()
{string s1("hello world");cout << s1.max_size() << endl;cout << s1.capacity() << endl;cout << s1.size() << endl;cout << s1.length() << endl;cout << s1 << endl;s1.reserve(100);cout << s1.capacity() << endl;cout << s1.size() << endl;cout << s1.length() << endl;s1.resize(20,'!');cout << s1.capacity() << endl;cout << s1.size() << endl;cout << s1.length() << endl;cout << s1 << endl;cout << s1.empty() << endl;s1.clear();cout << s1 << endl;cout << s1.empty() << endl;}

这里我们可以看到初始容量是15,那么达到它的容量了以后会怎么办呢?会进行扩容吗?

答案是会的,会自动扩容,初始容量为15,第一次扩容为31,之后按接近1.5倍的速率增长,

// 利用reserve提高插入数据的效率,避免增容带来的开销
//====================================================================================
void TestPushBack()
{string s;size_t sz = s.capacity();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';}}
}

我们如果知道容量,那么就可以在需要扩容之前用reverse来控制容量,因此我们是可以是利用reverse来提高插入效率的。

void Teststring2()
{// 注意:string类对象支持直接用cin和cout进行输入和输出string s("hello, bit!!!");cout << s.size() << endl;cout << s.length() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小s.clear();cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到10个,多出位置用'a'进行填充// “aaaaaaaaaa”s.resize(20, 'a');cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充// "aaaaaaaaaa\0\0\0\0\0"// 注意此时s中有效字符个数已经增加到15个s.resize(15);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;// 将s中有效字符个数缩小到5个s.resize(5);cout << s.size() << endl;cout << s.capacity() << endl;cout << s << endl;
}

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

3.【 string类对象的访问及遍历操作】

参考资料:

operator [ ] :http://www.cplusplus.com/reference/string/string/operator%5B%5D/

迭代器iteraor:

begin: http://www.cplusplus.com/reference/string/string/begin/

end:http://www.cplusplus.com/reference/string/string/end/

rbegin:http://www.cplusplus.com/reference/string/string/rbegin/

rend:http://www.cplusplus.com/reference/string/string/rend/

这里可以看一下具体使用方法:
 

void TestString3()
{string s3("hello world");const string s4("aaaaa");cout << s3[0] << endl;//可以直接使用[]打印cout << s4[0] << endl;//可以直接使用[]打印s3[0] = 'H';//可直接修改//s4[0] = 'A';//此时s4类似常量字符串,不能修改。// //1.For循环+operator[]
;	for (int i = 0; i < s3.size(); i++){cout << s3[i];}cout << endl;//2.迭代器进行遍历string::iterator it = s3.begin();/// <summary>/// 这里也可以替换为auto it=s3.begin/// </summary>while (it != s3.end()){cout << *it;it++;}cout << endl;string::reverse_iterator  rit = s3.rbegin();while (rit != s3.rend()){cout << *rit;rit++;}cout << endl;//3.范围for遍历 for (auto ch : s3){cout << ch ;}}int main()
{//TestString1();//TestString2();//TestPushBack();TestString3();return 0;
}

4. 【string类对象的修改操作】

push_back:http://www.cplusplus.com/reference/string/string/push_back/

append:http://www.cplusplus.com/reference/string/string/append/
operator+=:http://www.cplusplus.com/reference/string/string/operator+=/

c_str:http://www.cplusplus.com/reference/string/string/c_str/

find+npos:

http://www.cplusplus.com/reference/string/string/find/

http://www.cplusplus.com/reference/string/string/npos/

rfind:http://www.cplusplus.com/reference/string/string/rfind/

subster:http://www.cplusplus.com/reference/string/string/substr/

使用实例:

void TestString1()
{string s1("hello world");cout << s1 << endl;s1.push_back('a');cout << s1 << endl;s1.append("b");cout << s1 << endl;s1 += 'c';s1 += "abc";cout << s1 << endl;cout << s1.c_str() << endl;}

rfind默认从后向前寻找,rfind找的是字符串中某个字符最后出现的位置,而find找的是某个字符首次出现的位置,而当字符串中不包含某个字符时,就会返回npos

先面来看一个示例:

void TestString2()
{string file("Test.cpp.pp");size_t pos = file.rfind('.');cout << pos << endl;string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;string url("http://www.cplusplus.com/reference/string/string/find/");size_t start = url.find("/");if (start == string::npos){cout << "invalid url" << endl;return;}cout << start << endl;
}

下面是整体的使用用例:

void TestString3()
{// npos是string里面的一个静态成员变量// static const size_t npos = -1;// 取出url中的域名string url("http://www.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");if (start == string::npos){cout << "invalid url" << endl;return;}start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;// 删除url的协议前缀size_t pos = url.find("://");url.erase(0, pos + 3);cout << url << endl;
}

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

5.【string类非成员函数】

参考资料:

operator++:http://www.cplusplus.com/reference/string/string/operator+/

operator>>:http://www.cplusplus.com/reference/string/string/operator%3E%3E/

operator<<:http://www.cplusplus.com/reference/string/string/operator%3C%3C/

getline:http://www.cplusplus.com/reference/string/string/getline/

relational operators:http://www.cplusplus.com/reference/string/string/operators/

string类中还有一些其他的操作,这里不一一列举,大家在需要用到时不明白了查文档即可。

6.【vs和g++下string结构的说明】


【注意】:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
【vs下string的结构】:
string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:
当字符串长度小于16时,使用内部固定的字符数组来存放当字符串长度大于等于16时,从堆上开辟空间这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。

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

但是string类中仍然有一些缺陷:

STL 的string类怎么啦?_stl里为什么没讲string-CSDN博客

2.3【string的模拟实现】

我们为了跟更加深入了解string除了了解其使用方法以外,最好能够模拟实现string:
下面是代码部分:

【String.h部分】:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <stdbool.h>
#include <iostream>
using namespace std;
namespace str
{class String{public:void swap(str::String& s);const static size_t npos = -1;typedef char* iterator;typedef const char* const_iterator;String(const char* str = "");~String();String(const String& s);String& operator=(const str::String& s);size_t capacity() const;size_t size() const;const char* c_str() const;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;const char& operator[](size_t pos)const;void push_back(char ch);void append(const char* str);void reserve(size_t n);String& operator+=(char ch);String& operator+=(const char* str);void Insert(size_t pos, const char* str);void Insert(size_t pos, char ch);void erase(size_t pos, size_t len = npos);bool operator<(const String& s)const;bool operator==(const String& s)const;bool operator>(const String& s)const;bool operator<=(const String& s)const;bool operator!=(const String& s)const;bool operator>=(const String& s)const;void clear();void resize(size_t n, char ch = '\0');size_t find(char ch, size_t pos = 0);size_t find(const char* sub, size_t pos = 0);String substr(size_t pos, size_t len = npos);private:char* _str;size_t _size;size_t _capacity;};
}
ostream& operator<<(ostream& out, const str::String& s);
istream& operator>>(istream& in, str::String& s);

【String.cpp部分】:

#define _CRT_SECURE_NO_WARNINGS
#include "String.h"//void str::String::swap(str::String& s)
//{
//	std::swap(_str, s._str);//加上std的原因是防止其与库里的swap冲突,因此指定这里的swap是std库
//	std::swap(_size, s._size);//中的swap
//	std::swap(_capacity, s._capacity);
//}// s2(s1);
//str::String::String(const str::String& s)
//	:_str(nullptr)
//	, _size(0)
//	, _capacity(0)
//{
//	String tmp(s._str);//拷贝构造调用构造,构建出来的tmp再与this进行交换。
//	swap(tmp);
//}
// s2=s1;
//str::String& str::String::operator=(const str::String& s)
//{
//	if (this != &s)
//	{
//		String tmp(s);
//		//this->swap(tmp);
//		swap(tmp);
//	}
//
//	return *this;
//}// s2 = s3
//str::String& str::String::operator=(str::String tmp)//s3拷贝构造tmp
//{
//	swap(tmp);//交换tmp与s2
//
//	return *this;
//}str::String::String(const char* str):_size(strlen(str)),_capacity(_size)
{_str = new char[_capacity + 1];strcpy(_str, str);}
str::String::String(const str::String& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}
str::String& str::String::operator=(const str::String& s)
{if (this != &s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}str::String::~String()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
size_t str::String::capacity() const
{return _capacity;
}
size_t str::String::size() const
{return _size;
}
const char* str::String:: c_str() const
{return _str;
}
str::String::iterator str::String:: begin()
{return _str;
}
str::String::iterator str::String::end()
{return _str + _size;
}
str::String::const_iterator str::String::begin() const
{return _str;
}
str::String::const_iterator str::String::end() const
{return _str + _size;
}
const char& str::String::operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}
void str::String::reserve(size_t n)//按需调整空间
{if (_capacity < n)//当前容量小于指定容量就进行扩容,将数组的内容扩大。{char* tmp = new char[n + 1];//多开的1个空间是为了存储‘\0’strcpy(tmp, _str);//之后将原字符串中的内容拷贝到新空间delete[] _str;//并释放原空间_str = tmp;//将原数组指向新开辟的数组空间_capacity = n;//并更新容量}
}
void str::String::push_back(char ch)
{if (_size == _capacity)在进行尾插之前,要先判断是否需要扩容{reserve((_capacity == 0) ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';
}
void str::String::append(const char* str)
{size_t len = strlen(str);if (_size + len >= _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;
}
str::String& str::String::operator+=(char ch)
{push_back(ch);return *this;
}
str::String& str::String::operator+=(const char* str)
{append(str);return *this;
}
void str::String::Insert(size_t pos, char ch)
{if (_size == _capacity)//判断是否需要扩容{reserve((_capacity == 0) ? 4 : 2 * _capacity);}size_t end = _size + 1;//这里定义end位于‘\0’之后的一个位置while (end > pos)//将pos之后的数据全都向后移{_str[end] = _str[end-1];end--;}_str[pos] = ch;//将pos位置处放上需要插入的字符_size++;//最后将_size++即可/*int  end = _size;while (end >=(int) pos)//这里转换成int的原因是,pos为无符号整数,直接进行比较大小会导致end{//进行类型提升将end也当作无符号整数,当end--为-1时,由于end变为无符整形,-1=4294967295_str[end+1] = _str[end];//会大于0end--;}_str[pos] = ch;_size++;*/
}
bool str::String:: operator<(const String& s)const
{return strcmp(_str, s._str) < 0;
}bool str::String:: operator==(const String& s)const
{return strcmp(_str, s._str)== 0;
}bool str::String:: operator>(const String& s)const
{return strcmp(_str, s._str) > 0;
}bool str::String:: operator<=(const String& s) const
{return *this < s || *this == s;
}bool str::String::operator>=(const String& s) const
{return !(*this < s);
}bool str::String::operator!=(const String& s) const
{return !(*this == s);
}
void str::String::clear()
{_str[0] = '\0';_size = 0;
}
ostream& operator<<(ostream& out, const str::String& s)
{/*for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;*/for (auto ch : s)//范围for遍历字符串{out << ch;}return out;/*str::String::const_iterator it = s.begin();while (it != s.end()){out << *it;it++;}return out;*/}
istream& operator>>(istream& in, str::String& s)
{s.clear();//在流提取之前要先将字符串里之前的数据清空,不然提取到的新字符串就会尾插在旧数据char ch;//之后ch = in.get();//由于cin类似于scanf遇到空格就不会再进行读取了因此这里需要用到istream里的getchar buffer[129];//这里定义一个数组,用来存放读取到的字符,由于是在函数中创建的,不会占用size_t i = 0;//字符串的空间,跟随函数的生命周期while (ch != ' ' && ch != '\n'){buffer[i++] = ch;if (i == 128)//如果读取的字符太长数组都存满了,还没有读取结束{//就将获取到的字符先放入到字符串中,之后重新进行读取buffer[i] = '\0';s += buffer;i = 0;}ch = in.get();}if (i != 0)//如果字符不长,则可以直接将其放到字符串中{buffer[i] = '\0';s += buffer;}return in;
}void str::String::Insert(size_t pos, const char* str)
{assert(pos < _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(len + _size);}//int end = _size + len;//while (end > pos)//{//	_str[end] = _str[end - len];//	end--;//}int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];end --;}strncpy(_str + pos, str, len);_size += len;
}
void str::String::erase(size_t pos, size_t len )
{assert(pos < _size);if (len == npos || len + pos > _size){_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;}
}
void str::String::resize(size_t n, char ch)
{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);while (_size < n){_str[_size] = ch;++_size;}_str[_size] = '\0';}
}
size_t str::String::find(char ch, size_t pos)
{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}
size_t str::String::find(const char* sub, size_t pos)
{assert(pos < _size);const char* p = strstr(_str + pos, sub);if (p){return p - _str;}else{return npos;}
}
str::String str::String::substr(size_t pos, size_t len )
{assert(pos < _size);str::String s;size_t end = pos + len;if (len == npos || len + pos > _size){len = _size - pos;end = _size;}s.reserve(len);for (size_t i = pos; i < end; i++){s += _str[i];}return s;}

【Test.cpp部分】:

#define _CRT_SECURE_NO_WARNINGS
#include "String.h"
//void test_string1()
//{
//	str::String s1("hello world");
//	cout << s1.c_str() << endl;
//
//	str::String s2;
//	cout << s2.c_str() << endl;
//
//	for (size_t i = 0; i < s1.size(); i++)
//	{
//		cout << s1[i] << " ";
//	}
//	cout << endl;
//
//	str::String::iterator it = s1.begin();
//	while (it != s1.end())
//	{
//		(*it)++;
//		cout << *it << " ";
//		++it;
//	}
//	cout << endl;
//
//	for (auto& ch : s1)
//	{
//		ch++;
//		cout << ch << " ";
//	}
//	cout << endl;
//
//	cout << s1.c_str() << endl;
//}
//
void test_string2()
{str::String s1("hello world");cout << s1.c_str() << endl;s1.push_back(' ');s1.append("hello bit hello bit");cout << s1.c_str() << endl;s1 += '#';s1 += "*********************";cout << s1.c_str() << endl;str::String s2;s2 += '#';s2 += "*********************";cout << s2.c_str() << endl;
}void test_string3()
{str::String s1("hello world");cout << s1.c_str() << endl;s1.Insert(5, '%');cout << s1.c_str() << endl;s1.Insert(s1.size(), '%');cout << s1.c_str() << endl;s1.Insert(0, '%');cout << s1.c_str() << endl;
}void test_string4()
{str::String s1("hello world");str::String s2("hello world");cout << (s1 >= s2) << endl;//s1[0] = 'z';cout << (s1 >= s2) << endl;cout << s1 << endl;//cin >> s1;cout << s1 << endl;}void Test_string1()
{str::String s1("hello world");str::String s2;//cout << s1 << endl;//cout << s2 << endl;for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;str::String::iterator it = s1.begin();while (it != s1.end()){(*it)++;cout << *it << " ";++it;}cout << endl;for (auto& ch : s1){ch++;cout << ch << " ";}cout << endl;cout << s1.c_str() << endl;
}void test_string5()
{str::String s1("hello world");s1.Insert(5, "abc");cout << s1 << endl;s1.Insert(0, "xxx");cout << s1 << endl;s1.erase(0, 3);cout << s1 << endl;s1.erase(5, 100);cout << s1 << endl;s1.erase(2);cout << s1 << endl;
}void test_string6()
{str::String s1("hello world");cout << s1 << endl;s1.resize(5);cout << s1 << endl;s1.resize(25, 'x');cout << s1 << endl;
}void test_string7()
{str::String s1("test.cpp.tar.zip");//size_t i = s1.find('.');//size_t i = s1.rfind('.');//string s2 = s1.substr(i);//cout << s2 << endl;str::String s3("https://legacy.cplusplus.com/reference/string/string/rfind/");//string s3("ftp://www.baidu.com/?tn=65081411_1_oem_dg");// 协议// 域名// 资源名str::String sub1, sub2, sub3;size_t i1 = s3.find(':');if (i1 != str::String::npos)sub1 = s3.substr(0, i1);elsecout << "没有找到i1" << endl;size_t i2 = s3.find('/', i1 + 3);if (i2 != str::String::npos)sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));elsecout << "没有找到i2" << endl;sub3 = s3.substr(i2 + 1);cout << sub1 << endl;cout << sub2 << endl;cout << sub3 << endl;
}void test_string8()
{str::String s1("hello world");str::String s2 = s1;cout << s1 << endl;cout << s2 << endl;str::String s3("xxxxxxxxxxxxxxxxxxx");s2 = s3;cout << s2 << endl;cout << s3 << endl;
}void test_string9()
{str::String s1("hello world");cin >> s1;cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;
}int main()
{//Test_string1();//test_string1();//test_string2();//test_string3();//test_string4();//test_string6();//test_string7();//test_string8();//test_string9();return 0;
}

下面是老生常谈的问题了,也就是在调用拷贝构造时会出现的两个指针指向一块空间的情况,这样再调用析构函数时,就会导致同一块空间连续析构两次。

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

这里可以通过增加一个引用计数来解决,也就是说增加一个变量用来统计某块空间被多少个变量所引用。

那么什么是写时拷贝呢?

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

参考资料:

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

C++的std::string的“读时也拷贝”技术! | 酷 壳 - CoolShell


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

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

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

相关文章

Vue.js高效前端开发(增删查)

效果图 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><div id"app"><span>ID</span><input type"text" name"…

如何在Java中创建Excel表单控件

前言 在数据填报时&#xff0c;创建Excel表单控件是一项常见的任务&#xff0c;它可以极大地简化数据收集和处理的过程。传统的做法需要在Excel中开启开发工具&#xff0c;并且自己手动添加&#xff0c;如下图&#xff0c;就是一个常见的表单控件。 而在Java中&#xff0c;可…

Linux 设备树: of_property_match_string 的用法与工作原理

前言 当前新版本的 Linux 内核 设备驱动框架&#xff0c;与设备树&#xff08;Device Tree&#xff09;结合密切&#xff0c;整体 设备树的设备驱动框架&#xff0c;比较的庞大&#xff0c;但又非常的经典。 一个个的 设备树解析函数&#xff0c;都是前人【智慧】的结晶&#…

DC电源模块与其他电源模块有着明显的区别与优势

DC电源模块与其他电源模块有着明显的区别与优势 BOSHIDA DC电源模块&#xff08;Direct Current Power Supply Module&#xff09;是一种将交流电转换为直流电的设备&#xff0c;常用于电子设备和工业控制系统中。与其他电源模块相比&#xff0c;DC电源模块有一些明显的区别和…

R语言ggplot2 | 热图+随机森林重要性!升级版~

&#x1f4cb;文章目录 原图复现定义ggrf_ggcor_plot()函数加载数据集一键出图函数优点 今天推出一个升级版&#xff1a; ggrf_ggcor_plot的函数。只需要输入 响应变量的矩阵和 解释变量的矩阵&#xff0c;就能轻松一键生成随机森林重要性相关性热图。 原图 所需复现的随机森…

推荐5款测试数据生成工具!

一个成功、有效的测试策略由下面几个基本部分组成&#xff1a;完整的测试覆盖率、最小化的环境影响和健壮的测试数据。 其中测试数据尤其重要&#xff0c;其质量直接关系到测试的有效性。可以把测试数据看作是保持测试引擎运行的燃料——高质量的测试数据有助于确保测试执行的…

极速体验DolphinScheduler 3.2.1 Standalone 版[一]

文章目录 极速体验DolphinScheduler 3.2.1 Standalone 版前置准备工作启动 DolphinScheduler Standalone Server解压并启动 DolphinScheduler登录 DolphinScheduler 启停服务配置数据库 极速体验DolphinScheduler 3.2.1 Standalone 版 Standalone 仅适用于 DolphinScheduler 的…

【go从入门到精通】函数详解

作者简介&#xff1a; 高科&#xff0c;先后在 IBM PlatformComputing从事网格计算&#xff0c;淘米网&#xff0c;网易从事游戏服务器开发&#xff0c;拥有丰富的C&#xff0c;go等语言开发经验&#xff0c;mysql&#xff0c;mongo&#xff0c;redis等数据库&#xff0c;设计模…

【蓝桥杯】tarjan算法

一.概述 Tarjan 算法是基于DFS的算法&#xff0c;用于求解图的连通性问题。 Tarjan 算法可以在线性时间内求出&#xff1a; 无向图&#xff1a; 割点与桥双连通分量 有向图&#xff1a; 强连通分量必经点与必经边 1.割点&#xff1a; 若从图中删除节点 x 以及所有与 x 关联的…

Intel AIPC发布会:开启AI终端应用的新纪元

2024年3月27日下午&#xff0c;Intel在北京市朝阳区凤凰中心举办了AIPC发布会开启了AI终端应用的新征程。 整场发布会围绕着‘让不可想象&#xff0c;变为寻常’主线进行。在本次发布会上&#xff0c;众多PC端的AI应用得到了展示&#xff0c;包括&#xff1a;智谱AI&#xff…

第十一届蓝桥杯大赛第二场省赛试题 CC++ 研究生组-寻找2020

数据很恶心&#xff0c;但是考点挺友好~ 把测试数据黏贴到记事本中&#xff0c;知测试数据的行列数 然后根据规则判断2020是否出现&#xff0c;并累计其次数即可。 判断可能需要注意超出下标&#xff0c;可以索性把数组定大些。 #include<stdio.h> const int N 310; ch…

哈曼卡顿音箱解决关闭自动休眠 + 自用车载音乐分享制作

一&#xff1a;哈曼卡顿音箱解决关闭自动休眠 1. 背景&#xff1a;每天做最多的事情就是开音箱电源。问了客服&#xff0c;说只有玻璃4才能关闭休眠。搞得我都想买新音箱了。 2. 解决办法&#xff1a;电脑开机启动一个阻止功放休眠.exe&#xff0c;可以设置自动启动&#x…

Redis入门到实战-第十九弹

Redis实战热身Count-min-sketch篇 完整命令参考官网 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://redis.io/Redis概述 Redis是一个开源的&#xff08;采用BSD许可证&#xff09;&#xff0c;用作数据库、缓存、…

Android Studio 无法下载 gradle-7.3.3-bin.zip

下载新的Android Studio&#xff0c;然后创建新的工程时&#xff0c;出现报错&#xff1a;Could not install Gradle distribution from https://services.gradle.org/distributions/gradle-7.3.3-bin.zip 或者超时&#xff0c;我们可以复制&#xff1a;https://services.grad…

IntellIJ Idea 内存不足时怎么设置

文章目录 前言背景一、 内存显示二、 在IDEA中设置内存三 、在IDEA中打开内存的设置文件四、 JetBrains ToolBox 中安装 IntellIJ Idea配置文件位置总结 前言 请各大网友尊重本人原创知识分享&#xff0c;谨记本人博客&#xff1a;南国以南i、 提示&#xff1a;以下是本篇文章…

【React】react 使用 lazy 懒加载模式的组件写法,外面需要套一层 Loading 的提示加载组件

react 组件按需加载问题解决 1 错误信息2 解决方案 1 错误信息 react 项目在创建 router 路由时&#xff0c;使用 lazy 懒加载时&#xff0c;导致以下报错&#xff1a; The above error occurred in the <Route.Provider> component:Uncaught Error: A component suspe…

计算机基础系列 —— CPU

“Make everything as simple as possible, but no simpler.” – Albert Einstein 文中提到的所有实现都可以参考&#xff1a;nand2tetris_sol&#xff0c;但是最好还是自己学习课程实现一遍&#xff0c;理解更深刻。 之前的文章里我们介绍了 Register、PC、RAM 和 ALU&#…

【Linux实践室】Linux用户管理实战指南:用户密码管理操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;用户密码存放地及方式2.2 &#x1f514;使用…

游戏本续航@控制中心的省电模式效果如何

文章目录 节能模式长续航模式&#x1f47a;相关工具 节能模式长续航模式&#x1f47a; 蓝天模具Control Center中的模式 根据我的试验,以及软件的提示,可以发现 Power Saving是最省电的,儿Quiet模式并不省电,它会启用独立显卡,只不过风扇的转速不像娱乐模式和性能模式那么积极而…

UE5学习日记——蓝图节点前缀关键字整理

一、起因 节点如海&#xff0c;中英文翻译的时候还是有差别的&#xff0c;比如&#xff1a; 同一个中文&#xff0c;可能在英文里完全不同&#xff0c;连出现位置可能都不一样 附加 Attach Actor To Component&#xff08;将Actor附加到组件&#xff09;Append Array&#xf…