STL篇一:string

文章目录

  • 前言
  • 1. STL的简单理解
    • 1.1 什么是STL
    • 1.2 STL的版本
    • 1.3 STL的六大组件
    • 1.4 STL的重要性
    • 1.5 STL的缺陷
  • 2. string类
    • 2.1 为什么学习string类?
      • 2.1.1 C语言中的字符串
      • 2.1.2 两个面试题
    • 2.2 标准库中的string类
      • 2.2.1 string类(了解)
      • 2.2.2 string类的常用接口说明
    • 2.3 string类的模拟实现中的一些问题
      • 2.3.1 经典的string类问题
      • 2.3.2 浅拷贝
      • 2.3.3 深拷贝
    • 2.4 string的模拟实现
      • 2.4.1 构造函数
      • 2.4.2 拷贝构造
      • 2.4.3 析构函数
      • 2.4.4 赋值运算符重载
      • 2.4.5 [ ]的实现
      • 2.4.6 扩容
      • 2.4.7 尾插
      • 2.4.8 插入一个字符串
      • 2.4.9 += 的运算符重载
      • 2.4.10 插入
      • 2.4.11 删除
      • 2.4.12 重新设置大小
      • 2.4.13 << 与 >> 的重载
      • 2.4.14 < 、>、==运算符重载
      • 2.4.15 查找函数
  • 3.全部代码
    • 3.1 String.h
    • 3.2 String.cpp
    • 3.3 Test.cpp
  • 4. 总结

前言

  可能很多人都不太清楚STL是什么,它是用来干什么的,大家在看完这篇文章之后相信能有个大概的了解,目前我只能说STL是C++的中十分重要的部分之一,是大家学习C++必须掌握的部分。

1. STL的简单理解

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. 在笔试中

1. 二叉树层序打印
2. 重建二叉树
3. 两个栈实现一个队列

  1. 在面试中
    在这里插入图片描述
    3.在工作中

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

1.5 STL的缺陷

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

2. string类

  string类是一个容器,它是我们学习STL的第一部分,我们的主线是讲解STL的容器,其余部分会在讲解容器时进行穿插讲解。

2.1 为什么学习string类?

2.1.1 C语言中的字符串

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

2.1.2 两个面试题

  1. 字符串转整形数字
  2. 字符串相加
  在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

2.2 标准库中的string类

2.2.1 string类(了解)

  string类的文档介绍

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_trai ts和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.2 string类的常用接口说明

在这里插入图片描述
  上面就是关于string修改部分的一些接口,比如插入、删除等等。
在这里插入图片描述
  上面就是关于string容量部分的一些接口,比如计算长度、计算容量等等。
  此外还有大量的其他接口,再后面我会将用的比较多的接口进行模拟实现,如果有小伙伴对其他接口也十分感兴趣,可以通过下面的链接进行跳转观看string的全部接口,对于每一个接口点进去都会有详细的文档说明和测试用例,十分便于大家理解。

2.3 string类的模拟实现中的一些问题

2.3.1 经典的string类问题

  上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用String
class String
{
public:/*String():_str(new char[1]){*_str = '\0';}*///String(const char* str = "\0") 错误示范//String(const char* str = nullptr) 错误示范String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};
// 测试
void TestString()
{String s1("hello bit!!!");String s2(s1);
}

在这里插入图片描述

  我们会发现程序运行崩溃,这是因为上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。这个知识点我在C++类和对象中的赋值运算符部分进行过详细的讲解,有不懂的小伙伴可以点击链接进行跳转阅读。

2.3.2 浅拷贝

  浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
  可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

2.3.3 深拷贝

  如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
在这里插入图片描述
  深拷贝的具体如何实现在下一小节模拟实现中进行讲解。

2.4 string的模拟实现

2.4.1 构造函数

//声明,这一行是放在String.h文件中的
//string(const char* str = "");WY::string::string(const char* str):_size(strlen(str)),_capacity(strlen(str))
{_str = new char[_capacity + 1];strcpy(_str, str);
}

  在实现拷贝构造时一般都会采用缺省值的方式,是为了避免使用者在实例化对象时忘记赋予初始值。对于缺省参数,在声明和定义中,一般是在声明时书写缺省值。并且在开辟空间时是要开辟容量+1 个空间,因为参数的字符串中是包含一个字符零的,因此在开辟时要给字符零预留一个空间,

2.4.2 拷贝构造

WY::string::string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_capacity = s._capacity;_size = s._size;
}

  拷贝构造一定需要注意的点是参数必须进行引用传参,负责会出现无限递归的问题,具体过程在C++类和对象有关拷贝构造的小节有详细说明。
  初次之外这里也是涉及到了深拷贝,在拷贝构造中,我们是重新开辟了一块新空间进行拷贝,因此原来的对象与新对象指向的是不同的空间,在析构时是不会发生对一块空间析构两次的情况。

2.4.3 析构函数

//析构
WY::string::~string()
{delete[] _str;_str = nullptr;_capacity = _size = 0;
}

  就是释放空间,并将对象中的成员变量都进行归零。需要注意的是在释放空间是要加 [ ]中括号,对于单个变量是不需要加中括号的,但是对于多个变量,比如数组在释放时需要加中括号。详细原理见C/C++动态内存管理。

2.4.4 赋值运算符重载

WY::string& WY::string::operator = (const string& s)
{if (this != &s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_capacity = s._capacity;_size = s._size;}return *this;
}

  首先需要说明的是,这里的引用传参仅仅只是为了提高效率,因为在实现了拷贝构造函数之后再进行传值传参就不会发生无穷递归了。为了避免浅拷贝问题,需要重新开辟一块空间,将数据进行拷贝过去,然后将被赋值的对象的原数据进行释放,将新开辟的空间给了被赋值对象,再将其余成员变量依次进行修改。

2.4.5 [ ]的实现

char& WY::string::operator[](size_t index)
{return _str[index];
}const char& WY::string::operator[](size_t index)const
{return _str[index];
}

  中括号的实现可以让用户更加方便的查询数据,但是const成员是无法调用非const 的成员函数的,因此要将const成员函数也要实现一下。

2.4.6 扩容

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

  重新开辟一块空间再将原数据进行拷贝就可以了,要记得释放原对象的空间噢!!

2.4.7 尾插

void WY::string::push_back(char c)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = c;_size++;_str[_size] = '\0';
}

  尾插无法避免的情况就是当容量已满时就需要进行扩容。

2.4.8 插入一个字符串

void WY::string::append(const char* str)
{int len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size +=len;
}

  上面的尾插只是插入了一个字符,这个函数是用来专门插入一个字符串的。在库函数中这个函数是可以实现在各个位置进行插入,这里我只实现了尾部插入,需要更加详细了解的可以去看相关的需求文档。

2.4.9 += 的运算符重载

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

  这里的逻辑跟尾插一样,直接调用尾插就好了,这也是进行分模块实现功能的好处,可以进行代码复用,十分方便。

2.4.10 插入

void WY::string::insert(size_t pos, char c)
{assert(pos >= 0);if (_size == _capacity){reserve(_capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;
}

  只要插入数据就需要进行扩容判断,然后从后往前不断挪动数据,一直到pos位置再进行插入,这一块的时间复杂度是O(n)。

void WY::string::insert(size_t pos, const char* str)
{assert(pos >= 0);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + 1;while (end > pos){_str[end + len - 1] = _str[end - 1];end--;}strncpy(_str + pos, str, len);_size += len;
}

  插入一个字符串和插入一个字符的逻辑是一样的。

2.4.11 删除

void WY::string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len == npos ||pos + len > _size){_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[pos++] = _str[begin++];}_size -= len;}
}

  删除有两种情况,一种是将pos位置之后的数据全部删除,一种是只删除pos位置之后长度为len的数据。这里的npos是一个全局变量,是为了与库函数中的实现方式相对应,同时它也是参数len的缺省值。
  如果用户没有具体说明删除多长的数据,默认为删除pos位置之后的所有数据,而当你删除的数据长度加上pos位置大于整个数据的大小说明此时也是删除了pos位置之后的所有数据。另一种情况就是将需要删除的数据后面所剩的数据全部挪移到前面即可。

2.4.12 重新设置大小

void WY::string::resize(size_t n, char c)
{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);while (_size < n){_str[_size++] = c;}_str[_size] = '\0';}
}

  如果小于原数据大小,直接在n位置填上’\0’即可,如果大于原数据,就需要先扩容,再多余的空间上填上字符c,这里的c的默认参数是’\0’。

2.4.13 << 与 >> 的重载

ostream& WY::operator<<(ostream& _cout, const WY::string& s)
{for (size_t i = 0; i < s.size(); i++){_cout << s[i];}return _cout;
}istream& WY::operator>>(istream& _cin, WY::string& s)
{char ch = _cin.get();while (ch != ' ' && ch != '\n'){s += ch;ch = _cin.get();}return _cin;
}

  ostream与istream也是一个对象,一个是输出流(cout),一个是输入流对象(cin)。这个运算符重载需要注意的是这个是写在类外面的,并不是一个类的成员函数,因为如果写成成员函数的话,就是这个样子operator>>(this,istream& _cin),由于this是在前面,因此在外面调用的话就需要书写成 s << cout,而我们正常写的是cout << s,所有需要在类的外面进行声明定义。但是类外面的函数要是无法访问类内部的成员变量的,所以还需要将这两个函数声明为该类的友元函数,有关友元函数的讲解在C++类和对象中有详细解释。当一个函数被声明为友元函数时,这个函数就可以访问该类的成员变量。
  还需要注意的一个点是这两个函数一般都是引用返回,一是为了提高效率(引用传参、引用返回都不需要再进行拷贝构造,因此可以提高效率),二是可以实现连续输入输出。比如:cout << s << s1 ------>operator << (opeartor <<(cout,s),s1)

2.4.14 < 、>、==运算符重载

bool WY::string::operator<(const string& s)
{return strcmp(_str, s._str) < 0;
}bool WY::string::operator==(const string& s)
{return strcmp(_str, s._str) == 0;
}bool WY::string::operator<=(const string& s)
{return (*this < s) || (*this == s);
}bool WY::string::operator>(const string& s)
{return !(*this <= s);
}bool WY::string::operator>=(const string& s)
{return !(*this < s);
}bool WY::string::operator!=(const string& s)
{return !(*this == s);
}

  实现上两个其他的就可以直接进行复用了。

2.4.15 查找函数

size_t WY::string::find(char c, size_t pos) const
{for (size_t i = pos; i < size(); i++){if (_str[i] == c)return i;}return npos;
}size_t WY::string::find(const char* s, size_t pos) const
{const char* p = strstr(_str + pos, s);if (p){return p - _str;}else{return npos;}
}

  pos是用户可以指定从哪里开始进行查找,查找单个字符依次便利尽心比较即可。在查找子串时,可以调用库函数的strstr函数,如果找到它返回的是子串的首地址,如果没找到返回的是空指针。还需要注意的是指针减指针的含义是两个指针之间的数据个数,这里也就是子串首字母的位置

3.全部代码

3.1 String.h

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace WY
{class string{friend ostream& operator<<(ostream& _cout, const WY::string& s);friend istream& operator>>(istream& _cin, WY::string& s);public:typedef char* iterator;typedef const char* const_iterator;public:string(const char* str = "");string(const string& s);string& operator = (const string& s);~string();//// iteratoriterator begin();const_iterator begin()const;iterator end();const_iterator end()const;/// modifyvoid push_back(char c);string& operator+=(char c);void append(const char* str);string& operator+=(const char* str);void clear();void swap(string& s);const char* c_str()const;/// capacitysize_t size()const;size_t capacity()const;bool empty()const;void resize(size_t n, char c = '\0');void reserve(size_t n);/// accesschar& operator[](size_t index);const char& operator[](size_t index)const;///relational operatorsbool operator<(const string& s);bool operator<=(const string& s);bool operator>(const string& s);bool operator>=(const string& s);bool operator==(const string& s);bool operator!=(const string& s);// 返回c在string中第一次出现的位置size_t find(char c, size_t pos = 0) const;// 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos = 0) const;// 在pos位置上插入字符c/字符串str,并返回该字符的位置void insert(size_t pos, char c);void insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置void erase(size_t pos, size_t len = npos);private:char* _str;size_t _capacity;size_t _size;const static size_t npos;};ostream& operator<<(ostream& _cout, const WY::string& s);istream& operator>>(istream& _cin, WY::string& s);void test_string1();void test_string2();void test_string3();void test_string4();void test_string5();void test_string6();
};

3.2 String.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"String.h"const size_t WY::string::npos = -1;//构造
WY::string::string(const char* str):_size(strlen(str)),_capacity(strlen(str))
{_str = new char[_capacity + 1];strcpy(_str, str);
}//拷贝构造
WY::string::string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_capacity = s._capacity;_size = s._size;
}WY::string& WY::string::operator = (const string& s)
{if (this != &s){char* tmp = new char[s._capacity + 1 ];strcpy(tmp, s._str);delete[] _str;_str = tmp;_capacity = s._capacity;_size = s._size;}return *this;
}//析构
WY::string::~string()
{delete[] _str;_str = nullptr;_capacity = _size = 0;
}char& WY::string::operator[](size_t index)
{return _str[index];
}const char& WY::string::operator[](size_t index)const
{return _str[index];
}//扩容
void WY::string::reserve(size_t n)
{if (_capacity < n){_capacity = n;char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;}
}void WY::string::push_back(char c)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = c;_size++;_str[_size] = '\0';
}void WY::string::append(const char* str)
{int len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size +=len;
}const char* WY::string::c_str()const
{return _str;
}WY::string& WY::string::operator+=(char c)
{push_back(c);return *this;
}WY::string& WY::string::operator+=(const char* str)
{append(str);return *this;
}void WY::string::clear()
{_str[0] = '\0';
}void WY::string::swap(string& s)
{char* tmp = new char[_capacity + 1];strcpy(tmp, _str);strcpy(_str, s._str);strcpy(s._str, tmp);
}size_t WY::string::size()const
{return _size;
}size_t WY::string::capacity()const
{return _capacity;
}bool WY::string::empty()const
{return _size == 0;
}void WY::string::insert(size_t pos, char c)
{assert(pos >= 0);if (_size == _capacity){reserve(_capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;
}void WY::string::insert(size_t pos, const char* str)
{assert(pos >= 0);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + 1;while (end > pos){_str[end + len - 1] = _str[end - 1];end--;}strncpy(_str + pos, str, len);_size += len;
}void WY::string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len == npos ||pos + len > _size){_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[pos++] = _str[begin++];}_size -= len;}
}void WY::string::resize(size_t n, char c)
{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);while (_size < n){_str[_size++] = c;}_str[_size] = '\0';}
}WY::string::iterator WY::string::begin()
{return _str;
}WY::string::const_iterator WY::string::begin()const
{return _str;
}WY::string::iterator WY::string::end()
{return _str + _size;
}WY::string::const_iterator WY::string::end()const
{return _str + _size;
}// cout << s ----> operator <<(cout,s) 
ostream& WY::operator<<(ostream& _cout, const WY::string& s)
{for (size_t i = 0; i < s.size(); i++){_cout << s[i];}return _cout;
}istream& WY::operator>>(istream& _cin, WY::string& s)
{char ch = _cin.get();while (ch != ' ' && ch != '\n'){s += ch;ch = _cin.get();}return _cin;
}bool WY::string::operator<(const string& s)
{return strcmp(_str, s._str) < 0;
}bool WY::string::operator==(const string& s)
{return strcmp(_str, s._str) == 0;
}bool WY::string::operator<=(const string& s)
{return (*this < s) || (*this == s);
}bool WY::string::operator>(const string& s)
{return !(*this <= s);
}bool WY::string::operator>=(const string& s)
{return !(*this < s);
}bool WY::string::operator!=(const string& s)
{return !(*this == s);
}size_t WY::string::find(char c, size_t pos) const
{for (size_t i = pos; i < size(); i++){if (_str[i] == c)return i;}return npos;
}size_t WY::string::find(const char* s, size_t pos) const
{const char* p = strstr(_str + pos, s);if (p){return p - _str;}else{return npos;}
}

3.3 Test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"String.h"void WY::test_string1()
{string s("hello world");cout << s.c_str() << endl;string s1(s);cout << s1.c_str() << endl;string s2;s2 = s;cout << s2.c_str() << endl;s.push_back('x');cout << s.c_str() << endl;s1.append("xxx");cout << s1.c_str() << endl;s2 += 'x';cout << s2.c_str() << endl;s2 += "xxx";cout << s2.c_str() << endl;}void WY::test_string2()
{string s("hello world");s.insert(0, 'x');cout << s.c_str() << endl;string s1("hello world");s1.insert(0, "xxx");cout << s1.c_str() << endl;s.erase(0, 1);cout << s.c_str() << endl;s1.erase(0, 3);cout << s1.c_str() << endl;s1.erase(1);cout << s1.c_str() << endl;}void WY::test_string3()
{string s("hello world");s.resize(5);cout << s.c_str() << endl;s.resize(20,'x');cout << s.c_str() << endl;
}void WY::test_string4()
{string s;cin >> s;cout << s << endl;
}void WY::test_string5()
{string s("hello world");string s1("hello world");cout << (s == s1) << endl;string s2("hello");cout << (s == s2) << endl;string s3("a");string s4("b");cout << (s3 < s4) << endl;
}void WY::test_string6()
{string s("hello world");size_t pos = s.find('w');cout << pos << endl;
}int main()
{WY::test_string1();//WY::test_string2();//WY::test_string3();//WY::test_string4();//WY::test_string5();//WY::test_string6();return 0;
}

4. 总结

  STL是学习是C++中必不可少的一部分,必须熟练掌握才行,我建议大家可以通过刷题来快速掌握STL的使用,熟练使用是最重要的,其次才是了解它的底层实现。
  如果大家发现有什么错误的地方,可以私信或者评论区指出喔。我会继续深入学习C++,希望能与大家共同进步,那么本期就到此结束,让我们下期再见!!觉得不错可以点个赞以示鼓励!!

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

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

相关文章

Kafka基本介绍

消息队列 产生背景 消息队列&#xff1a;指的数据在一个容器中&#xff0c;从容器中一端传递到另一端的过程 消息(message): 指的是数据&#xff0c;只不过这个数据存在一定流动状态 队列(queue): 指的容器&#xff0c;可以存储数据&#xff0c;只不过这个容器具备FIFO(先进…

kali_linux换源教程

vim /etc/apt/sources.list #阿里云deb http://mirrors.aliyun.com/kali kali-rolling main non-free contribdeb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib#清华大学源deb http://mirrors.tuna.tsinghua.edu.cn/kali kali-rolling main contrib…

Android14实战:打破音频默认重采样的限制(五十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

5文件操作

包含头文件<fstream> 操作文件三大类&#xff1a; ofstream : 写文件ifstream &#xff1a;读文件fstream : 读写文件 5.1文本文件 -文件以ascii的形式存储在计算机中 5.1.1写文件 步骤&#xff1a; 包含头文件 #include "fstream"创建流对象 ofs…

【STM32】STM32学习笔记-FlyMCU串口下载和STLINK Utility(30)

00. 目录 文章目录 00. 目录01. 串口简介02. 串口连接电路图03. FlyMCU软件下载程序04. 串口下载原理05. FlyMCU软件其它操作06. STLINK Utility软件07. 软件下载08. 附录 01. 串口简介 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式&#xff0c;因为它简…

WebRTC入门:基础的核心协议与概念(二十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

【C#】C#实现PDF合并

文章目录 一、下载iTextSharp.dll下载iTextSharp.dll命名空间引入 二、界面设计三、代码全局变量选择文件夹的按钮确认合并的按钮 四、导出结果五、完整源码 一、下载iTextSharp.dll 下载iTextSharp.dll 可使用联机方式或者文件下载方式。 命名空间引入 代码开始时引入了一…

Android Studio导入项目 下载gradle很慢或连接超时

AS最常见的问题之一就是下载gradle非常慢&#xff0c;还经常出现下载失败的情况&#xff0c;没有gradle就无法build项目&#xff0c;所以一定要先解决gradle的下载问题&#xff0c;下面教大家两种常用方法。 因为我的项目绝大多数使用的是gradle-5.6.4-all&#xff0c;下面就以…

【搜索引擎设计:信息搜索怎么避免大海捞针?

在前面我们提到了网页爬虫设计&#xff1a;如何下载千亿级网页&#xff1f;中&#xff0c;我们讨论了大型分布式网络爬虫的架构设计&#xff0c;但是网络爬虫只是从互联网获取信息&#xff0c;海量的互联网信息如何呈现给用户&#xff0c;还需要使用搜索引擎完成。因此&#xf…

MYSQL分表分库 详解

目录 一、垂直拆分于水平拆分的区别&#xff1f; 垂直拆分 水平拆分 二、分表分库有哪些策略&#xff1f; Hash分片策略 枚举分片策略 日期分片策略 范围分片策略&#xff08;用的较多&#xff09; 三、分表分库之后&#xff0c;如何查询的呢&#xff1f; 四、分表分…

【RHEL】Vivado调用VCS+Verdi联合仿真报错解决

问题描述 在使用VCS Verdi仿真Vivado工程时&#xff0c;点击行为仿真按钮进度条窗口消失后&#xff0c;Verdi窗口并未出现&#xff0c;查看消息报错如下&#xff1a; vcs: line 34205: 119837 Segmentation fault (core dumped) ${TOOL_HOME}/bin/cfs_ident_exec -f ${X…

网络安全已死,趁早转行?

近年来&#xff0c;曾经被寄予厚望的网络安全行业似乎正逐渐失去昔日的辉煌。曾经一度备受瞩目的网络安全专业&#xff0c;如今却面临着降薪、裁员的困境。许多公司对网络安全的重视程度不高&#xff0c;网络安全岗位成了背锅的代名词。在这样的环境下&#xff0c;有人开始质疑…

智能小车项目(七)通过PID实现给定和实际速度值计算PWM输出

我们先看大脑&#xff08;上位机nano&#xff09; keybord_ctrl节点发布’cmd_vel’消息消息类型为Twist队列大小为1 pub rospy.Publisher(cmd_vel, Twist, queue_size1)if not stop: pub.publish(twist)driver_node订阅这个消息 当有消息时cmd_vel_callback回掉函数处理消息…

感染嗜肺军团菌是什么感觉?

记录一下最近生病的一次经历吧&#xff0c;可能加我好友的朋友注意到了&#xff0c;前几天我发了个圈&#xff0c;有热心的朋友还专门私信了我说明了他自己的情况和治疗经验&#xff0c;感谢他们。 ​ 那么关于这次生病的经历&#xff0c;给大家分享一下。 首先&#xff0c;这次…

redis夯实之路-持久化之RDB与AOF详解

数据库 初始化服务器时会根据redisServer的dbnum属性来决定创建多少个数据库&#xff0c;默认为16 使用select切换数据库 客服端状态redisClient结构的db属性记录了当前的目标数据库 RedisDb结构的dict字典保存了数据库的所有键值对&#xff0c;这个字典被称为键空间。 cru…

C++I/O流——(2)预定义格式的输入/输出(第二节)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 含泪播种的人一定能含笑收获&#xff…

【MySQL】MySQL表的约束-空属性/默认值/列属性/zerofill/主键/自增长/唯一键/外键

文章目录 表的约束1.空属性 --null && not null2.默认值 -- default3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键 表的约束 表的约束&#xff1a;表中一定要有各种约束&#xff0c;通过约束&#xff0c;让我们未来插入数据库表中的数据是符合预期的。约束的本质是…

【QT】多层QTreeWidget与QStackedWidget的关联操作

通过点击多层QTreeWidget来控制QStackedWidget中的page页面切换 treeWidget设计 treeWidget设计&#xff1a; // treeWidget设计ui->treeWidget->clear();ui->treeWidget->setColumnCount(1);//第一层QStringList l;l<<"管理系统";QTreeWid…

iPhone“查找”最多可添加32个物品!

对于那些丢三落四的果粉来说&#xff0c;苹果的“查找”功能是一大福音。不管是丢失了iPhone、iPad、Mac、AirPods还是AirTag&#xff0c;都可以通过“查找”功能在地图上追踪设备的位置&#xff0c;甚至是远程锁定或抹掉设备的数据。 那么&#xff0c;iPhone的查找一次能支持添…

LeetCode 38 外观数列

题目描述 外观数列 给定一个正整数 n &#xff0c;输出外观数列的第 n 项。 「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定义的数字字符串序列&#xff1a; countAndSay(1) "1…