STL概览

 标准模版库

一、IO

  iostream:控制台输入输出,istreamostream

  fstream:文件输入输出,ifstreamostream

  stringstreamstring输入输出,istringstreamostringstream

 

 

1-1 IO类图

 

头文件

类型

对象

<iostream>

istreamwistream从流读取数据

ostreamwostream向流写入数据

iostreamwiostream读写流

cin:标准输入

cout:标准输出

cerr:标准错误

clog:标准错误

<fstream>

ifstreamwifstream从文件读取数据

ofstreamwofstream向文件写入数据

fstreamwfstream读写文件

 

<sstream>

istringstreamwistringstreamstring读取数据

ostringstreamwostringstreamstring写入数据

stringstreamwstringstream读写string

 

1-1 标准IO

  1. iostream

  流类型

  cin:标准输入(srandard input)流的istream类对象,一般设备对应键盘;

  cout:标准输出(srandard output)流的ostream类对象,一般设备对应显示屏;

  cerr:标准错误(srandard error)流的ostream类对象,一般设备对应显示屏;

  clog:标准错误(srandard error)流的ostream类对象,与cerr区别是cerr不经过缓冲区,直接向显示器输出,clog中的信息默认会存放在缓冲区,缓冲区满或者遇到endl时才输出;

  输入主要由一组重载的左移操作符<<来完成,输出主要由重载的右移操作符>>完成,原型为:

  ostream& operator <<(ostream &out, int source);

  ostream& operator <<(ostream &out, char *ps);

  …

  以上函数是对ostream类对应的每个基本数据类型都有其友元函数进行了重载,返回的是一个流对象的引用,引用可以作为左值使用,使用时就能构成连续输出。

  istream& operator >>(istream &in, int source);

  istream& operator >>(istream &in, char *ps);

  …

  流状态

  io_state是一个枚举类型,机器相关的整形别名,代表一种类型,由各个IO类定义,提供表达条件状态的完整功能。

ios::io_state

功能

说明

ios::goodbit

标志流未出现错误

此值保证为0

ios::eofbit

标志到达文件结尾

到达文件结束位置,eofbitfailbit都会被置位

ios::failbit

标志IO操作失败

可恢复的错误,流还可以继续使用

ios::badbit

标志流已崩溃

badbit是系统级错误,不可恢复的读写错误,badbit置位后流就无法继续使用

1-2 io_state

  流状态相关的函数,bad()fail()eof()good()返回对应流状态,good默认为0

  clear()将流中的所有状态值都重设为有效状态;

  clear(strm::iostate flag)将流中指定条件状态置为有效;

  setstate(strm::iostate flag)给流添加指定条件;

  rdstate()返回流的当前条件。

  注意事项:

      A.由于IO对象不能复制或赋值,因此不能将形参或返回类型设置为流类型或把流存储在容器中,通常是引用方式传递和返回流;

      B.读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的;

      C.C++默认cincout关联,通过tie(NULL)函数可以解除cincout的绑定;为了兼容C,保证在使用std::printfstd::cout不发生混乱,C++默认将输出流绑到了一起,通过sync_with_stdio(false)函数可以解除Cstdio绑定,加快执行效率;

      D.每个IO对象都管理一个缓冲区,用于存储程序读写的数据,缓冲区刷新情况:程序结束、缓冲区满、关联输入和输出中需要输入时、flushendl(换行+刷新)、ends(空格+刷新)、用unitbuf操作符设置流的内部状态,使得流在每次输出操作符执行完都清空缓冲区,恢复系统默认管理方式用nounitbuf

  2. fstream

  文件打开

  void open(const char* filename, int mode, int access);

  open_mode是一个枚举类型,提供打开文件方式

  ifstream默认打开文件方式ios::in

  ofstream默认打开文件方式ios::out | ios::trunc

  fstream默认打开文件方式ios::in | ios::out

ios::open_mode

功能

说明

ios::app

追加方式打开文件

包含ios::ate属性,仅仅在out没有设定时才能用,此模式下,文件总是以输出方式被打开

ios::ate

打开后定位到文件末尾

可用于任何文件流对象,与其它模式可随意组合

ios::binary

以二进制方式打开文件

缺省的方式是文本方式,可用于任何文件流对象,与其它模式可随意组合

ios::in

以输入方式打开文件

仅仅能够对ifstreamfstream对象设定

iso::out

以输出方式打开文件

仅仅能够对ofstreamfstream对象设定,包含ios::trunc属性,需要保留out模式打开文件的内容,需指定app

ios::nocreate

不建立文件

文件不存在时打开失败

ios::noreplace

不覆盖文件

文件存在时打开失败

ios::trunc

文件存在则长度设为0

仅仅有out设定时才能设定

1-3 open_mode

  打开文件的属性取值:1普通文件,2只读文件,3隐含文件,4系统文件,属性值可以叠加。

  文件定位

  C++文件系统管理着两个与文件相关联的指针

  读指针:说明下一次读在文件中的位置

  写指针:说明下一次写在文件中的位置

  每次执行了读或者写操作后,这两个指针的值都会自动变化,对应的读写这两个指针的函数:

  streampos istream::tellg();    //返回读指针当前的值

  streampos ostream::tellp();    //返回写指针当前的值

  istream::seekg(streampos pos);    //设置下次读的位置

  istream::seekg(streamoff offset, seek_dir origin);

  ostream::seekp(streampos pos);    //设置下次写的位置

  ostream::seekp(streamoff offset, seek_dir origin);

  peek()函数用于读取并返下一个字符,但并不提取该字符串到输入流中

  seek_dir也是一个枚举类型,有三种:

  ios::beg文件开头,ios::cur文件当前位置,ios::end文件结尾

  文件读写

       A.文本文件读写

  插入器<<向文件输出,析取器>>从文件输入

  B.二进制文件读写

  ofstream& put(char ch);    //函数向流写入一个字符

  ifstream& get(char& ch);    //从流读取一个字符,结果保存在ch中,如果到文件结尾,返回空字符

  int get()    //从流返回一个字符,如果到达文件结尾,返回EOF

  ifstream& get(char *buf, int num, char delim = “\n”) //把字符串读入由buf指向的数组,直到读入num个字符或遇到由delim指定的字符;

  C.读写数据块

  读写二进制数据块,使用成员函数read()write()

  read(unsigned char* buf, int num);    //从文件读取num个字符带buf指向的缓存中

  write(const unsigned char *buf, int num);    //buf指向的缓存写入num个字符到文件中

  注意事项

    A.使用eof()循环读取文件时,读到结束标志EOF时不会立刻返回true,只是eofbit置位,下次调用eof()才返回true

    B.只有一个流处理无错状态时,才可以读写数据,使用前最好做检测判断;

    C.缓存同步情况:文件关闭、缓存满时、显示调用sync()

    D.一旦一个文件流已经被打开,它就保持与相应文件的关联,对一个已打开的文件流调用open会失败,并会导致failbit被置位,试图使用文件流的操作都会失败。

  3. stringstream

    stringstream使用简单,常见用法是在多种数据类型之间实现自动格式化。

二、string

string类型本质是一个模版类,用基本类型char实例化模版类basic_string得到一个具体的模版类。标准库中stringstringwstring两种,分别封装了charwchar_t,下面以string做详细介绍。

  1.  声明

  typedef base_string<char, char_traits<char>, allocator<char> > string;

  typedef base_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> > wstring;

  template <class charT, class traits = char_traits <charT>, class Alloc = allocator<charT> > class base_string;

  template <class charT> struct char_traits;

  template <class T> class allocator;

  参数注释:charT基本数据类型;char_traits指定字符属性,并且提供作用在字符或字符序列上的某些操作的特定语义;allocator分配器,用于定义标准库部分内容,提供内存模型。

  2. 原理

  base_string成员:

  _M_length:字符串长度

  _M_capacity:最大容量

  _M_refcount:引用计数,copy-on-write方式来减少无意义的内存拷贝

  _M_p:实际数据,当调用string::data()string::c_str()时返回该指针

Ø basic_string()默认构造

  template<typname _CharT, typname _Traits, typname _Alloc>

  inline basic_string<_CharT, _Traits, _Alloc>::basic_string()

  #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0

    : _M_dataplus(_S_empty_rep()._M_refdata(), _Alloc) { }

  #else

    : _M_dataplus(_S_construct(size_type(), _CharT(), _Alloc), _Alloc) { }

  宏_GLIBCXX_FULLY_DYNAMIC_STRING决定是否使用动态string,也就是不使用引用计数,总是拷贝内存,此宏默认值为0,也就是std::string默认使用引用计数策略。

  _M_dataplus_Alloc_hider类型,_Alloc_hide是分配器Alloc的子类,含有唯一的成员变量_M_p,指向string的数据部分。

  _Alloc_hider构造函数第一个参数是一个cha*指针,由_S_construct()函数返回,_S_construct()是理解string构造机制的关键,它有几个重载版本,主要作用就是根据输入的参数来构造一个string的内存区域,并返回该内存的指针,此指针并不是string内存空间的起始地址,这里调用_S_construct()版本为:

  _CharT* basic_string<_CharT, _Traits, _Alloc>::_S_construct(size_type _n, _CharT _c, _Alloc& _a)

  {

  // Check for out_of_range and length_error exceptions

  _Rep* __r = _Rep::_s_create(__n, size_type(0), __a);

  if(__n)

      _M_assign(__r->_M_refdata(), __n, __c);

 

  -__r->_M_set_length_and_sharable(__n);

  retun __r->_M_refdata();

  }

  该函数前两个参数__n__c,用于_s_create构造一个内存空间,并用__n__c字符来初始化,_s_create返回一个_Rep指针,_Rep定义如下:

  struct _Rep_base

  {

  size_type _M_length;

  size_type _M_capacity;

  _Atomic_word _M_refcount;

  }

  struct _Rep : _Rep_base

  {

  _charT* _M_refdata() throw()

  { return reinterpret_cast<_charT*>(this+1); }

 

  static _Rep* _S_create(size_type, size_type, const _Alloc&);

  …

  }

  _Rep中还有三个静态成员

l  static size_type _S_max_size = (((npos - sizeof(_Rep_base))/sizeof(_CharT)) - 1) / 4;  // 字符串最大长度,npos定义在类模版basic_string中为-1,即0xFFFFFFFF

l  static _CharT _S_terminal = ‘\0’;  // 字符串结束标志

l  static size_type _S_empty_rep_storage[(sizeof(_Rep_base) + sizeof(_CharT) + sizeof(size_type) -1) / sizeof(size_type)];

  // 这并不是一个0长度的数组,0长度的数组在编译时并不分配空间,仅仅作为占位符,初始化时为0,结果可表示空串有一个引用

  template<typname _CharT, typname _Traits, typname _Alloc>

  typname basic_string<_CharT, _Traits, _Alloc>::_Rep*

  basic_string<_CharT, _Traits, _Alloc>::_Rep::

  _S_create(size_type __capacity, size_type __old_capacity, const _Alloc& __alloc)

  {

  const size_type __pagesize = 4096;  //内存分配最小单位

  const size_type __subpagesize = 128;  //分配的字符串以__subpagesize对齐

  const size_type __malloc_header_size = 4 * sizeof(void*);  // 内存头大小

  const size_type __page_capacity = ((__pagesize - __malloc_header_size – sizeof(_Rep) – sizeof(_ChaT)) / sizeof(_ChaT));  // 对于小内存增长,乘以2,优化内存开辟性能,此处优化与malloc机制有关

  if(__capacity > __old_capacity && __capacity < 2 * __old_capacity && __capacity > __page_capacity)

      __capacity = 2 * __old_capacity;

   

  // 初步计算需要开辟内存大小,__capacity + 1多开辟一个单位内存存储字符串结束符

  size_type __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);

  

  // 页面对齐调整,加上malloc头部长度

  const size_type __adj_size = __size + __malloc_header_size;

  // 页面对齐,重新计算出sizecapacity

  if(__adj_size > __pagesize && __capacity > __old_capacity)

  {

      const size_type __extra = __pagesize - __adj_size % __pagesize;

      __capacity += __extra / sizeof(_CharT);

     if(__capacity > _S_max_size)

          __capacity = _S_max_size;

      __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);

  }

  else if(__size > __subpagesize)

  {

      const size_type __extra = __subpagesize - __adj_size % __subpagesize;

      __capacity += __extra / sizeof(_CharT);

      __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);

  }

 

  // 开辟内存,开启并初始化引用计数为0

  // 上面说的引用计数宏开关_GLIBCXX_FULLY_DYNAMIC_STRING跟这里不冲突

  void* __place = _Raw_bytes_alloc(_alloc).allocate(__size);

  _Rep *__p = new (__place) _Rep;

  __p->_M_capacity = __capacity;

  __p->_M_set_sharable();

  __p->_M_length = 0;

  return __p;

  }

  前面提到的base_string的几个成员_M_length_M_capacity_M_refcount并不是直接作为string对象的成员,而是通过_Rep来管理这些变量,这样string只需要保存一个_Rep指针即可,最大限度减小string对象大小,减小对象拷贝的消耗。_M_refdata()用来获取指向数据部分的指针,this+1就是从起始地址向后加8*3个字节(_Atomic_word4个字节,考虑字节对齐sizeof(_Rep) = 24),再回顾string copy-on-write机制

    A.写时复制。读操作时浅拷贝,复制数据地址,引用计数自增,写操作时深拷贝,重新开辟空间,引用计数重置;

    B.内存释放。多个对象共享一段内存,涉及到内存释放时机,就需要一个引用计数,当引用计数为零时释放内存;

    C.满足多线程安全。C++要求所有内建类型具有相同级别的线程安全性,即多线程读同一对象时是安全的,多线程写同一类型的不同对象时是安全的,而对于使用引用计数的string则不然,不同的string对象可能共享同一个引用计数,写操作会修改该引用计数,如果不加保护,会造成多线程不一致,解决方法:引用计数用原子操作(即要么操作成功,要么什么也不做),一般来说原子操作都是系统提供的,也可以通过汇编代码控制CPU完成。下面对两种方法分别做示例介绍:

l  用系统提供的mutexatomlock等操作实现:

int __exchange_and_add(volatile _Atomic_word* __mem, int __val)

{

    __glibcxx_mutex_lock(__gnu_internal::atomic_mutex);

    int __result = *__mem;

    *__mem += __val;

    __glibcxx_mutex_unlock(__gnu_internal::atomic_mutex);

    return __result;

l  X86架构的CPU,在C/C++中嵌套汇编实现:

int __exchange_and_add(volatile _Atomic_word* __mem, int __val)

{

  register int __result;

// lock汇编指令前缀,表示后面的指令在CPU上操作是串行完成,当CPU中的控// 制器检测到这个前缀时候,就会锁定内存总线,直到该指令执行完毕,再此期间

// 其它CPU不能访问这条指令所访问的内存单元

  __asm__ __volatile__(“lock; xaddl %0, %2” //完成加法,0表示result2表示*__mem

: “=r”(__result))  // 表示输出

: “0” (__val)       // 表示输入,__result__val在同一寄存器中

: “m” (*__mem)  // 表示在内存中

: “memory”       // 表示约定限制

       return __result;

}

  stringCOW并不是C++标准规定的,因此,不同平台不同版本会有不同实现,gcc4.x版本,都采用此机制,gcc5已经放弃使用COW的设计,采用短字节优化方案,即对长度小于16的字符串,作为string对象的一部分,直接从栈空间开辟内存,而且C++11std::move的引入也使COW的优化不再必要。

Ø  basic_string()拷贝构造

  template<typname _CharT, typname _Traits, typname _Alloc> basic_string<_CharT, _Traits, _Alloc>::basic_string(const basic_string& __str)

  : _M_dataplus(__str._M_rep()->_M_grap(_Alloc(__str.get_allocator())) , __str.get_allocator(), __str.get_allocator()) { }

  _CharT* _M_grap(const _Alloc& __alloc1, const _Alloc& __alloc2)

  {

       return (!_M_is_leaked() && __alloc1 == __alloc2) ? _M_refcopy() : _M_clone(__alloc1);

  }

  _M_is_leaked()判断是否是leak状态,取决于引用计数_M_refcount的值:

l  -1:没有引用。当调用写操作,但尚未copy内存时,为此状态

l  0:引用对象的个数为1。独立构造对象时,初始状态为此

l  >0:引用对象的个数为n+1。拷贝构造或赋值时,为此状态

  leak状态就是_M_refcount-1的状态,当为非leak状态且分配器相同时只返回引用,否则拷贝内存

Ø  basic_string()析构

~basic_string()

{

    _M_rep->_M_dispose(this->get_allocator());

}

_M_dispose函数作用是减少引用计数并决定是否释放空间

void _M_dispose(const _Alloc& __a)

{

  if(__builtin_expect(this != &_S_empty_rep(), false))

if(__gnu_cxx::__exchange_and_add(&this->_M_refcount, - 1) <= 0)

    _M_destory(__a);

}

 

_M_destory(const _Alloc& __a) throw()

{

  if(this == &_S_empty_rep())

      return;

  // 如果不是空串释放空间,__size才是真正释放的大小

  const size_type __size = sizeof(_Rep_base) + (this->_M_capacity + 1) * sizeof(_CharT);

  _Raw_bytes_alloc(__a).deallocate(reinterpret_cast<char*>(this*), __size);

}

  3. 用法

由于篇幅原因,下面各函数源码就不做具体分析,参考如表2-1

类型

相关函数

功能说明

 

 

 

构造(string太长会抛出length_error异常)

string ();

默认构造函数,如:string s;默认构造一个空的s

string& operator = (const string& str);

赋值函数,如:string s = “hi”;

string (const string& str);

拷贝构造函数

string (const string& str, size_t pos, size_t len = npos);

str内始于pos且长度顶多npos的部分作为字符串的初值

string (const char* s);

c的字符串初始化

string (const char* s, size_t n);

s的前n个字符初始化

string (size_t n, char c);

n个字符c初始化

template <class InputIter>

string (InputIter first, InputIter last);

区间first-last(不包含last)内的字符作为字符串的初值

 

 

 

 

 

 

运算符

赋值类运算符:=++=

string& operator += (const string& s);…

stingchar*char的各种版本

逻辑类运算符:==!=<<=>>=

bool operator == (const string& lhs, const string& rhs);

bool operator == (const string& lhs, const char* rhs);

bool operator == (const char* lhs, const string& rhs);…

stingchar*的组合版本

其它:[]<<>>

istream& operator >> (istream&, string& s);

ostream& operator << (ostream&, const string& s);

 

 

 

 

 

 

赋值

=

 

string& assign(const char* s);

C类型字符串赋值

string& assign(const char* s, int n);

s的前n个字符赋值给当前string

string& assign(int n, char c);

n个字符c字符赋值给当前string

string& assign(const string& s);

 

string& assign(const string& s, int pos, int n);

s中从pos开始的前n个字符赋值给当前string

string& assign(const_iterator first, const_iterator last)

区间first- last(不包含last)内的字符赋值给当前string

 

 

 

 

 

 

连接

+=

 

string& append(const char* s);

当前string结尾连接字符串s

string& append(const char* s, int n);

当前string结尾连接sn个字符

string& append(int n, char c);

当前string结尾添加n个字符c

string& append(const string& s);

 

string& append(const string& s, int pos, int n);

 

string& append(const_iterator first, const_iterator last)

 

void push_back(char c);

添加字符到字符串末尾

 

 

 

 

 

比较

==!=<<=>>=

 

int compare(const string& s) const;

比较当前strings的大小,>是返回1==时返回0<时返回-1

int compare(const char* s) const;

int compare(int pos, int n, const string& s) const;

比较当前stringpos开始的n个字符串与s的大小

int compare(int pos, int n, const char* s) const;

int compare(int pos, int n, const string& s, int pos2, int n2) const;

比较比较当前string中从pos开始的n个字符串与s中从pos2开始的n2个字符串的大小

int compare(int pos, int n, const char* s, int pos2) const;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

查找

int find(char c, int pos = 0) const;

当前stringpos位置开始向后查找字符c或字符串s,成功返回所在位置,失败返回string::npos的值

int find(const char* s, int pos = 0) const;

int find(const char* s, int pos, int n) const;

int find(const string s, int pos = 0) const;

int rfind(char c, int pos = npos) const;

当前stringpos位置开始向前查找字符c或字符串s,成功返回所在位置,失败返回string::npos的值

int rfind(const char* s, int pos = npos) const;

int rfind(const char* s, int pos, int n = npos) const;

int rfind(const string& s, int pos = npos) const;

int find_first_of(char c, int pos = 0) const;

当前stringpos位置开始向后查找字符c或字符串s第一次出现的位置,成功返回所在位置,失败返回string::npos的值

int find_first_of(const char* s, int pos, int n) const;

int find_first_of(const char* s, int pos = 0) const;

int find_first_of(const string& s, int pos = 0) const;

int find_first_not_of(char c, int pos = 0) const;

当前stringpos位置开始向后查找第一个不在字符c或字符串s中出现的位置,成功返回所在位置,失败返回string::npos的值

int find_first_ not_of(const char* s, int pos, int n) const;

int find_first_ not_of(const char* s, int pos = 0) const;

int find_first_ not_of(const string& s, int pos = 0) const;

int find_last_of(char c, int pos = 0) const;

find_first_of类似,只是反向查找,成功返回所在位置,失败返回string::npos的值

int find_last_of(const char* s, int pos, int n) const;

int find_last_of(const char* s, int pos = 0) const;

int find_last_of(const string& s, int pos = 0) const;

int find_last_not_of(char c, int pos = 0) const;

find_first_of类似,只是反向查找,成功返回所在位置,失败返回string::npos的值

int find_last_ not_of(const char* s, int pos, int n) const;

int find_last_ not_of(const char* s, int pos = 0) const;

int find_last_ not_of(const string& s, int pos = 0) const;

 

 

 

 

 

 

 

 

 

 

替换

string& replace(int p0, int n0, const char* s);

当前string中删除从p0开始的n0个字符,然后在p0处插入字符串s

string& replace(int p0, int n0, const char* s, int n1);

string& replace(int p0, int n0, const string& s);

string& replace(int p0, int n0, const string& s, int pos, int n1);

string& replace(int p0, int n0, int n, char c);

string& replace(iterator first, iterator last, const char* s);

当前string[first, last)之间的部分替换为字符串s

string& replace(iterator first, iterator last, const char* s, int n);

string& replace(iterator first, iterator last, const string& s);

string& replace(iterator first, iterator last, int n,char c);

string& replace(iterator first, iterator last, const_iterator first1, const_ iterator last1);

 

 

 

 

 

 

插入

string& insert(int p0, const char* s);

p0处插入字符串s

string& insert(int p0, const char* s, int n);

string& insert(int p0, const string& s);

string& insert(int p0, const string& s, int pos, int n);

string& insert(int p0, int n, char c);

p0处插入n个字符c

iterator insert(iterator it, char c);

it处插入字符c,返回插入后迭代器的位置

void insert(iterator it, const_iterator first, const_iterator last);

it处插入[first, last)之间的字符

void insert(iterator it, int n, char c);

it处插入n个字符c

 

 

 

删除

iterator erase(iterator first, iterator last);

删除[first, last)之间的字符,返回删除后迭代器的位置

iterator erase(iterator it);

删除it指向的字符,返回删除后迭代器的位置

string& erase(int pos = 0, int n = npos);

删除pos开始的n个字符,返回修改后的字符串

void pop_back();

删除字符串末尾字符

 

 

 

 

 

字符操作

const char& operator[](int n) const;

char& operator[](int n);

获取字符串中第n个位置的字符,[]不提供范围检查,但at提供,越界时会抛出out_of_range异常

const char& at(int n) const;

char& at(int n);

char& back();

返回最后一个字符

char& front();

返回第一个字符

const char* data() const;

获取一个非null终止的C字符串

const char* c_str() const;

获取一个以null终止的C字符串

int copy(char* s, int n, int pos = 0) const;

pos位置开始的n个字符拷贝到以s为起始位置的字符数组中,返回实际拷贝的数目

 

 

 

 

特性描叙

int capacity() const;

获取字符串的容量

int max_size() const;

获取可存放的最大字符串长度

int size() const;

获取字符串的容器大小

int length() const;

获取字符串的序列长度

bool emtpy() const;

获取字符串是否为空

void clear()

清空字符串

void resize(int len, char c);

设置字符串的大小为len,并用字符c填充不足部分

void reserve(size_type size = 0);

设置字符串的容量

void shrink_to_fit();

请求移除未使用的存储空间

 

 

 

迭代器

iterator begin();

iterator end();

string提供了向前和向后遍历的迭代器,能访问各个字符,迭代器不检查范围,有string::iteratorstring::const_iterator和反向查找string::reverse_iteratorstring::const_reverse_iterator四种

const_iterator cbegin();

const_iterator cend();

reverse_iterator rbegin();

reverse_iterator rend();

const_reverse_iterator crbegin();

const_reverse_iterator crend();

 

 

其它

allocator_type get_allocator() const;

返回与当前容器关联的内存分配器对象的一份拷贝

void swap(string& s);

交换当前strings的值

string substr(int pos = 0, int n = npos) const;

获取从pos开始的n个字符组成的字符串

非成员函数

istream& getline(istream& is, string& str, char delim = ‘\n’);

is流获取一行

2-1 string函数用法

  4. 注意事项

  char*赋值给string,如果不指定长度,默认以\0截断,如果指定长度超过char*字符串的长度,用std::cout系列函数输出时,会把不属于该char*之后的内存值打印出来,而对于printf系列的函数来打印string时遇到\0会截断。

三、STL容器

容器定义:在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对象的指针,这种对象类型叫做容器。可以理解为保存其它对象的对象,这种对象包含一系列处理其它对象的方法。

容器分类:STL容器是类模版与算法的集合,都是基于模版实现的,容器可以分为顺序容器、关联容器、无序关联容器、适配器容器、相接容器五类,几种容器类型之间的比较可参考表3-1

容器类型

容器名称

说明

特点

 

 

 

 

 

顺序容器

arrayC++11

固定大小的数组

支持快速随机访问,不能添加或删除元素。

vector

动态数组,内存连续

支持快速随机访问,尾部插入或删除快,尾部之外的位置插入或删除速度随操作元素到vector结尾的距离线性变化。

string

vector相似,专门用于保存字符

list

双向链表,内存不连续

链表任何位置插入或删除操作都很快,复杂度为O(1),不支持随机访问,list支持双向顺序访问,forward_list只支持单向顺序访问。

forward_listC++11

单向链表,内存不连续

deque

双端队列,vectorlist折中方案

支持快速随机访问,在头尾位置插入或删除速度快,中间位置插入或删除速度随数据量线性变化。

 

 

关联容器

map

关联数组,保存键-值对,键唯一

内部实现采用非线性的二叉树结构,支持高效的关键字查找和访问,查找复杂度为O(log n),但插入或删除时需要排序, setmap保证数据唯一性。

multimap

键可重复的map

set

只保存键且唯一

multiset

键可重复的set

 

 

 

无序关联容器

 

unordered_mapC++11

无序的map,键唯一

内部实现为哈希函数,无需为元素排序。提供快速查找(均摊O(1),最坏情况O(n)的复杂度)

unordered_multimapC++11

键可重复

unordered_setC++11

无序的set,键唯一

unordered_multisetC++11

键可重复

适配器容器

stack

栈,先进后出

只能访问栈顶元素,没有迭代器

queue

队列,先进先出

只能访问队首元素,没有迭代器

priority_queue

优先队列,先进先出

只能访问队首元素,没有迭代器

相接容器

spanC++20

相接的对象序列上的非占有视图

 

3-1 STL容器比较

容器线程安全:C++要求所有内建类型具有相同级别的线程安全性,即多线程读同一对象时是安全的,多线程写同一类型的不同对象时是安全的。

Ø  线程安全

l  多个读取者是安全的。多个线程可以同时读取同一容器的内容(期间不能写);

l  不同容器的多个写入者是线程安全的。多个线程可以同时写不同的容器;

l  多个线程同时修改同一容器中不同元素是线程安全的。除了std::vector<bool>的元素,如std::future对象的vector能从多个线程接受值;

l  同一容器上的元素可以同时由不指定为访问这些元素的函数修改。

Ø  线程不安全

l  在对同一个容器进行多线程的读写、写操作时。

Ø  线程安全实现

要实现完全的线程安全的容器这将很难,如果是那样,程序员可以不用自己做并行控制,毫无疑问这将带来很多方便,但这也非常难实现。一个库可以试图以下列方式实现这样完全线程安全的容器:

l  在每次调用容器的成员函数期间都要锁定该容器;

l  在每个容器返回的迭代器的生存期之内都要锁定该容器;

l  在每个容器调用的算法执行期间锁定该容器(这事实上没有意义,因为算法没有办法识别出它们正在操作的容器,它的教育意义在于看看为什么即使是有可能的它也不能工作)。

C库函数线程安全:C语言中大部分库函数(可重入函数)都是线程安全的,但也有几个常用函数是线程不安全的,也叫不可重入函数(函数使用了某些全局变量或静态变量,多线程调用时,导致并发读写)。常见的线程不安全类型有两类:一类是函数依赖了全局变量,并且会修改全局变量;另一类是函数返回了静态变量。

线程不安全函数

函数说明

线程不安全类型

unix线程安全版本

rand

产生随机整数

1

rand_r

strtok

字符串split

1

strtok_r

asctime

asc时间

2

asctime_r

ctime

时间戳

2

ctime_r

gethostbyaddr

返回主机信息

2

gethostbyaddr_r

gethostbyname

返回主机信息

2

gethostbyname_r

Inet_ntoa

转成IP

2

暂无

localtime

返回本地时间

2

localtime_r

3-2 C线程不安全函数

  1. 1.顺序容器

顺序容器也称为序列式容器,是一种各元素之间有顺序关系的线性表,是一种线性结构的可续集群。顺序容器中的每个元素均有固定的位置,除非用删除或插入操作改变这个位置。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。

   A. array

定义:template<class T, size::size_t N>struct array; 在头文件<array>中,其语义等同于保有一个C风格数组T[N],但又不同于C风格数组,它不会自动退化为T*

底层数据结构是固定大小的数组,由于大小固定,不支持添加和删除元素或改变容器大小等其它容器拥有的操作,在定义一个array容器的时候必须指定大小。array内存分配策略和基本内置类型一样,而vector则是在自由存储区上分配。

l  作为函数局部对象,则从栈上获得内存;

l  使用new操作符分配内存,则从堆上分配内存;

l  作为全局变量,则存储在全局存储区;

l  作为静态变量,则存储在静态存储区。

类型

相关函数

功能说明

 

 

元素访问

at

访问指定元素,同时进行越界检查

operator[]

访问指定元素,不进行越界检查

front

访问第一个元素

back

访问最后一个元素

data

返回指向内存中数组第一个元素的指针

 

特性描叙

empty

检查容器是否为空

size

返回容纳的元素数

max_size

返回可容纳的最大元素数

 

 

 

迭代器

begin

返回指向容器第一个元素的迭代器

cbegin

end

返回指向容器尾端元素的迭代器

cend

rbegin

返回指向容器最后元素的逆向迭代器

crbegin

rend

返回指向容器前端的逆向迭代器

crend

其它

fill

以指定值填充容器

swap

交换内容

非成员函数

operator ==!=<<=>>=

重载的比较函数,按照字典顺序比较

std::get(std::array)

访问array的第一个元素

std::swap(std::array) C++11

特化std::swap算法

辅助类

std::tuple_size<std::array>

获取array大小

std::tuple_element<std::array>

获取array元素类型

3-3 array函数用法

   B. vector

定义:template<class T, class Allocator = std::allocator<T>>class vector; 在头文件<vector >中。

底层数据结构是动态数组,提供随机访问迭代器(Randow Access Iterators),支持快速随机访问,灵活性上也有最大的缺点,当有新元素插入导致空间不够时,需经历“找到更大的连续内存空间à把数据复制到新空间à销毁旧空间”三部曲,另外,vector维护的是一段连续的内存空间,在尾部之外的位置插入或删除元素时,会引起后面元素向前或向后移动一个位置,发生移动的元素迭代器都会失效。

内存分配策略上以最小的代价连续存储元素,STL分配策略为:原大小为0,则配置1个元素大小,后面每次需要重新分配时,重新分配的大小为原来的两倍,这样就能预留一些空间,不必为每个新添加的元素进行一次内存分配。

类型

相关函数

功能说明

构造

operator=

 

assign

 

 

 

元素访问

at

访问指定元素,同时进行越界检查

operator[]

访问指定元素,不进行越界检查

front

访问第一个元素

back

访问最后一个元素

data

返回指向内存中数组第一个元素的指针

 

 

特性描叙

empty

检查容器是否为空

size

返回容纳的元素数

max_size

返回可容纳的最大元素数

capacity

返回当前存储空间能够容纳的元素数

reserve

预留存储空间

resize

改变容器中可存储元素的个数

shrink_to_fitC++11

通过释放未使用的内存减少内存的使用

 

 

 

迭代器

begin

返回指向容器第一个元素的迭代器

cbegin

end

返回指向容器尾端元素的迭代器

cend

rbegin

返回指向容器最后元素的逆向迭代器

crbegin

rend

返回指向容器前端的逆向迭代器

crend

 

 

 

修改器

clear

清除内容

insert

插入内容

erase

擦除元素

emplaceC++11

原位置构造元素

emplace_backC++11

在容器末尾就地构造元素

push_back

将元素添加到容器末尾

pop_back

移除末尾元素

其它

get_allocator

返回相关的分配器

swap

交换内容

 

 

非成员函数

operator ==!=<<=>>=

重载的比较函数,按照字典顺序比较

erase(std::vector) C++20

擦除所有满足特定判别标准的元素

erase_if(std::vector) C++20

std::swap(std::array) C++11

特化std::swap算法

3-4 vector函数用法

   C. list

定义:template<class T, class Allocator = std::allocator<T>>class list; 在头文件<list>中。

底层数据结构为双向循环链表,提供双向迭代器,能双向遍历,支持任意位置常数时间的插入或删除操作,不支持快速随机访问,只有在删除元素之后,指向删除元素的那个迭代器才失效。list内存分配策略如普通双向链表,有多少元素申请多少内存,不像vector那样需要预留空间供新元素的分配,也不会因为找不到连续的空间而引起整个容器的内存迁移。

类型

相关函数

功能说明

构造

operator=

 

assign

 

元素访问

front

访问第一个元素

back

访问最后一个元素

 

 

特性描叙

empty

检查容器是否为空

size

返回容纳的元素数

max_size

返回可容纳的最大元素数

reserve

预留存储空间

resize

改变容器中可存储元素的个数

 

 

 

迭代器

begin

返回指向容器第一个元素的迭代器

cbegin

end

返回指向容器尾端元素的迭代器

cend

rbegin

返回指向容器最后元素的逆向迭代器

crbegin

rend

返回指向容器前端的逆向迭代器

crend

 

 

 

 

 

修改器

clear

清除内容

insert

插入内容

erase

擦除元素

emplaceC++11

原位置构造元素

emplace_backC++11

在容器末尾就地构造元素

emplace_frontC++11

在容器头部就地构造元素

push_back

将元素添加到容器末尾

push_front

将元素添加到容器起始

pop_back

移除末尾元素

pop_front

移除头部元素

 

 

 

操作

merge

合并两个已排序列表

splice

从另一个list中移动元素

remove

移除满足特定标准的元素

remove_if

reverse

将该链表的所有元素的顺序反转

unique

删除连续重复的元素

sort

对元素进行排序

其它

get_allocator

返回相关的分配器

swap

交换内容

 

非成员函数

operator ==!=<<=>>=

重载的比较函数,按照字典顺序比较

erase(std::vector) C++20

擦除所有满足特定判别标准的元素

erase_if(std::vector) C++20

std::swap(std::array) C++11

特化std::swap算法

3-5 list函数用法

   D. forward_list

定义:template<class T, class Allocator = std::allocator<T>>class forward_list;在头文件<forward_list>中。

底层数据结构是单向链表,只支持前向遍历元素,与list的主要区别是没有反方向的迭代器,也正因此,forward_list的每个节点都节省了迭代器大小的开销,比list消耗少得多的内存,另外forward_list也因此有两个特殊点,是唯一一个不提供size()的容器,因为计算forward_list长度需要线性的时间开销,也是唯一一个在给定位置之后插入新元素的容器。

类型

相关函数

功能说明

构造

operator=

 

assign

 

元素访问

front

访问第一个元素

 

特性描叙

empty

检查容器是否为空

size

返回容纳的元素数

resize

改变容器中可存储元素的个数

 

 

 

迭代器

before_begin

返回指向容器第一个元素之前的迭代器

before_cbegin

begin

返回指向容器第一个元素的迭代器

cbegin

end

返回指向容器尾端元素的迭代器

cend

 

 

 

修改器

clear

清除内容

insert_after

在某个元素后插入内容

erase_after

擦除某个元素后的元素

emplace_after

在某个元素后原位构造元素

emplace_front

在容器头部就地构造元素

push_front

插入元素到容器起始

pop_front

移除首元素

 

 

 

操作

merge

合并两个已排序列表

splice_after

从另一个forward_list移动元素

remove

移除满足特定标准的元素

remove_if

reverse

将该链表的所有元素的顺序反转

unique

删除连续重复的元素

sort

对元素进行排序

其它

get_allocator

返回相关的分配器

swap

交换内容

 

 

非成员函数

operator ==!=<<=>>=

重载的比较函数,按照字典顺序比较

erase(std::vector) C++20

擦除所有满足特定判别标准的元素

erase_if(std::vector) C++20

std::swap(std::array) C++11

特化std::swap算法

3-6 forward_list函数用法

   E. deque

定义:template<class T, class Allocator = std::allocator<T>>class deque; 在头文件<deque>中。

是一种双向开口的一段一段的连续空间链接起来的数据结构,即在头尾两端都可做元素的插入或删除操作,支持元素的快速随机访问,是vectorlist的折中方案。

deque由一段一段连续空间链接而成,一旦需要在头尾增加新空间,便配置一定量的连续空间,并将该空间串接在deque的头部或尾部,deque一段一段的连续空间采用map(非STL中的map)作为主控,map是一块小的连续空间,其中每个元素都是指针,指向一块较大的线性连续空间,称为缓冲区,而缓冲区才是存储deque元素的空间主体。

类型

相关函数

功能说明

构造

operator=

 

 

 

 

 

元素访问

at

访问指定元素,同时进行越界检查

operator[]

访问指定元素,不进行越界检查

front

访问第一个元素

back

访问最后一个元素

 

 

特性描叙

empty

检查容器是否为空

size

返回容纳的元素数

max_size

返回可容纳的最大元素数

resize

改变容器中可存储元素的个数

shrink_to_fitC++11

通过释放未使用的内存减少内存的使用

 

 

 

迭代器

begin

返回指向容器第一个元素的迭代器

cbegin

end

返回指向容器尾端元素的迭代器

cend

rbegin

返回指向容器最后元素的逆向迭代器

crbegin

rend

返回指向容器前端的逆向迭代器

crend

 

 

 

修改器

clear

清除内容

insert

插入内容

erase

擦除元素

emplaceC++11

原位置构造元素

emplace_backC++11

在容器末尾就地构造元素

emplace_frontC++11

在容器头部就地构造元素

push_back

将元素添加到容器末尾

push_front

将元素添加到容器起始

pop_back

移除末尾元素

pop_front

移除头部元素

其它

get_allocator

返回相关的分配器

swap

交换内容

 

 

非成员函数

operator ==!=<<=>>=

重载的比较函数,按照字典顺序比较

erase(std::vector) C++20

擦除所有满足特定判别标准的元素

erase_if(std::vector) C++20

std::swap(std::array) C++11

特化std::swap算法

3-7 deque函数用法

  1. 2.关联容器

关联式容器是非线性的二叉树结构,各元素之间没有严格的物理上的顺序关系,但是关联容器提供了一致根据元素特点排序的功能。元素是有序的集合,默认在插入的时候按升序排列。

   A. map

   B. multimap

   C. set

   D. multiset

  1. 3.无序关联容器

与关联容器mapset功能类似,但内部实现完全不同,mapset的实现方式为红黑树,unordered_mapunordered_set不需要为容器排序,内部实现为哈希函数。

         A. unordered_map

         B. unordered_multimap

         C. unordered_set

         D. unordered_multiset

  1. 4.适配器容器

本质上适配器是使一种不同的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。适配器是容器的接口,本身不直接保存元素,它保存元素的机制是另一种顺序容器去实现。

    A. stack

定义:template<class T, class Container = std::deque<T>>class stack; 在头文件< stack >中。

栈是一种先进后出的数据结构,默认以双端队列deque作为底部容器,通过push/pop接口对栈顶元素进行操作。

类型

相关函数

功能说明

构造

operator=

 

 

 

元素访问

top

访问栈顶元素

特性描叙

empty

检查底层容器是否为空

size

返回容纳的元素数

 

修改器

emplaceC++11

于顶原位构造元素

push

向栈顶插入元素

pop

删除栈顶的元素

swap

交换内容

非成员函数

operator ==!=<<=>>=

比较函数,按照字典顺序比较

std::swap(std:: stack)

特化std::swap算法

辅助类

sd::uses_allocatorstd<std::stack>C++11

特化sd::uses_allocatorstd类型特性

3-12 stack函数用法

       B. queue

定义:template<class T, class Container = std::deque<T>>class queue; 在头文件<queue>中。

队列是先进先出的数据结构,默认以双端队列deque作为底部容器,通过push向队尾压入元素,pop从队首弹出元素。

类型

相关函数

功能说明

构造

operator=

 

 

 

元素访问

front

访问第一个元素

back

访问最后一个元素

特性描叙

empty

检查底层容器是否为空

size

返回容纳的元素数

 

修改器

emplaceC++11

于尾部原位构造元素

push

向队列尾部元素

pop

删除第一个元素

swap

交换内容

非成员函数

operator ==!=<<=>>=

比较函数,按照字典顺序比较

std::swap(std:: queue) C++11

特化std::swap算法

辅助类

sd::uses_allocator<std:: queue>C++11

特化sd::uses_allocatorstd类型特性

3-13 queue函数用法

       C. priority_queue

  定义:template<class T, class Container = std::vector<T>, class Compare = std::less<typename Container::value_type>>class priority_queue; 在头文件<queue>中。

优先级队列是一种有权重观念的队列,例如在以整数大小作为衡量的权重定义下,priority_queue总是弹出最大的数,默认以大顶堆(max-heap)作为底部容器。

类型

相关函数

功能说明

构造

operator=

 

 

 

元素访问

top

访问栈顶元素

特性描叙

empty

检查底层的容器是否为空

size

返回容纳的元素数

 

修改器

emplaceC++11

原位构造元素并排序底层容器

push

插入元素并对底层容器排序

pop

删除第一个元素

swap

交换内容

非成员函数

std::swap(std::priority_queue)

特化std::swap算法

辅助类

sd::uses_allocatorstd<std:: priority_queue>C++11

特化sd::uses_allocatorstd类型特性

3-14 priority_queue函数用法

  1. 5.相接容器

    A. span

定义:template<class T, std::ptrdiff_t Extent = std::dynamic_extent>class span; 在头文件<span>中。

  Span所描叙的对象能指代对象的相接序列,序列的首元素在零位置。span能拥有静态长度,该情况下序列中的元素已知并编码于类型中,或拥有动态长度。

类型

相关函数

功能说明

构造

operator=

 

 

 

元素访问

operator[]

访问序列的元素

operator()

 

 

 

迭代器

begin

返回指向序列起始的迭代器

cbegin

end

返回指向序列末尾的迭代器

cend

rbegin

返回指向序列逆向起始的逆向迭代器

crbegin

rend

返回指向序列逆向末尾的逆向迭代器

crend

 

观察器

size

返回序列中的元素数

size_bytes

返回以字节表示的序列大小

empty

检查序列是否为空

 

子视图

first

获得由序列首N个元素组成的子段

last

获得由序列末N个元素组成的子段

subspan

获得字段

 

非成员函数

begin

指向span开始的迭代器

end

指向span结尾的迭代器

as_bytes

转换span为对其底层字节的视图

as_writable_bytes

3-15 span函数用法

 四、小结

  对于STL的学习,应循序渐进,首先了解其干嘛的,有哪些东西,对你有什么帮助,使用时如何选择合适的容器进行高效使用。

 

参考资料:

www.cplusplus.com/reference/string

www.zh.cppreference.com/w.cpp.container

www.classfoo.com

 

作者:博客园博主 KeepHopes,对大数据、人工智能领域颇感兴趣,请多多赐教!
原文链接:https://www.cnblogs.com/yuwanxian/articles/10890729.html
版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议。转载请注明出处,谢谢!
posted @ 2019-05-19 20:49  ywx-super  阅读(354)  评论(0)    收藏  举报