注:原创不易,转载请务必注明原作者和出处,感谢支持!

注:内容来自某培训课程,不一定完全正确!

一 STL基本概念

STL(Standard Template Library)标准模板库,最早是惠普实验室开发的一系列软件的统称,现在主要出现在C++中,但是在引入C++之前该技术已经存在很长的时间了。

STL从广义上分为:容器(container),算法(alogrithm)和迭代器(iterator)。容器和算法之间通过迭代器进行无缝连接。STL几乎所有的代码都采用了模板类或者模板函数,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。

在C++标准库当中,隶属于STL的占到了80%以上。在C++标准库中,STL被组织成以下13个头文件:

<algorithm>
<deque>
<functional>
<iterator>
<vector>
<list>
<map>
<memory>
<numeric>
<queue>
<set>
<stack>
<utility>

STL的优点有哪些?

(1)STL是C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。

(2)STL的一个重要的特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分离确实使得STL变得非常通用。例如在STL的vector容器中,可以放入元素,基础数据类型变量,元素的地址;STL的sort()排序函数可以用来操作vector, list等容器。

(3)程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。

(4)STL具有高可重用性,高性能,高移植性,跨平台的优点

  1. 高可重用性:STL中几乎所有的代码都采用了模板类和模板函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用的机会。
  2. 高性能:如map可以高效地从十万条记录里查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平衡二叉树的一种)
  3. 高移植性:在项目A上用STL编写的模块,可以直接移植到项目B上
  4. 跨平台:windows的visual studio上编写的代码可以在Mac OS的XCode上直接编译

C++中的容器是指用来存放数据的类模板。容器分为两种,分别是序列式容器和关联式容器。它们的区别如下:

序列式容器:容器的元素的位置是由进入容器时机和地点来决定的

关联式容器:容器有其自身的规则,进入容器元素的位置不是由进入的时机和地点决定的

迭代器可以理解为指针,对指针的操作基本都可以对迭代器操作。实际上,迭代器是一个类模板,这个类模板封装了一个指针。迭代器一般用来遍历容器中的元素。

算法可以理解为解决问题有限步骤。STL提供了大约100个实现算法的模板函数,比如算法for_each将为指定序列中的每个元素调用指定的函数等。这样一来,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法函数,就可以完成所需要的功能并大大地提升效率。

从下面这个小例子可以窥见容器、迭代器和算法三者之间的关系。

// 算法,负责统计某个元素的个数
int MyCount(int *begin, int *end, int val)
{
int cnt = 0;
while (begin != end)
{
if (*begin == val)
++cnt;
++begin;
} return cnt;
} int main()
{
// 数组,容器
int arr[] = { 0, 7, 5, 4, 9, 2, 0 };
// 迭代器,开始和结束指针
int *pbegin = arr;
int *pend = &(arr[sizeof(arr) / sizeof(int)]); int num = MyCount(pbegin, pend, 0);
cout << "num = " << num << endl; getchar();
return 0;
}

下面的案例揭示了STL的基本使用语法。

void PrintVector(int v)
{
cout << v << endl;
} // STL基本语法
void Test()
{
// 实例化一个包含int类型元素的容器
vector<int> v; v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40); // STL提供的for_each算法
// 容器提供迭代器
vector<int>::iterator pBegin = v.begin();
vector<int>::iterator pEnd = v.end();
for_each(v.begin(), v.end(), PrintVector);
}

下面的案例演示了容器包含自定义类型的应用案例。

class Person
{
public:
Person() = default;
Person(int age, int id) : age(age), id(id) {}
void Show()
{
cout << "(age, id) = (" << age << ", " << id << ")" << endl;
}
private:
int age;
int id;
}; void Test2()
{
vector<Person> v;
v.push_back(Person(10, 20));
v.push_back(Person(30, 40));
v.push_back(Person(50, 60)); for (vector<Person>::iterator it = v.begin(); it != v.end(); ++it)
{
it->Show();
} }

二 string容器

string的特性

说到string的特性,就不得不和char *类型的字符串进行对比:

(1)char *是一个指针,而string是一个类。string封装了char *,管理这个字符串,是一个char *型的容器。

(2)string封装了很多实用的成员方法。查找find,拷贝copy,删除delete,替换replace和插入insert等

(3)string不用考虑内存释放和越界问题。string管理char *所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。

string转成char *,实用成员方法c_str()char *转string,直接将char *传入string的构造方法中即可生成相应的string对象。

下面是string的初始化,赋值和取值操作

// string的初始化
void Test1()
{
string s1; // 无参构造,为空字符串
string s2(10, 'a');
string s3("hello");
string s4(s3); // 拷贝构造 cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
} // string的赋值操作
void Test2()
{
string s1;
string s2("app");
s1 = s2; // 重载等号运算符
cout << s1 << endl;
s1 = 'a';
cout << s1 << endl;
s1.assign("jkl"); // 赋值的成员方法assign
cout << s1 << endl;
} // 取值操作
void Test3()
{
string s1 = "abcdefg"; // 重载[]操作符
for (int i = 0; i < s1.size(); ++i)
{
cout << s1[i] << endl;
} // 成员方法at()
for (int i = 0; i < s1.size(); ++i)
{
cout << s1.at(i) << endl;
} // 两者区别
// []方式,如果访问越界,程序直接崩溃
// at()方式,如果访问越界,程序抛出out_of_range异常 try
{
// 直接奔溃
// cout << s1[100] << endl; // 抛出异常
cout << s1.at(100) << endl;
}
catch (...) // 捕获所有异常
{
cerr << "越界了!" << endl;
}
}

string的拼接操作

// 重载+=操作符
string &operator+=(const string &str);
string &operator+=(const char *str);
string &operator+=(const char c); // append()方法
// 把字符串s连接到当前字符串尾部
string &append(const char *s);
// 把字符串s的前n个字符连接到当前字符串结尾
string &append(const char *s, int n);
// 同operator+=()
string &append(const string &str);
// 把字符串s中从pos开始的n个字符串连接到当前字符串结尾
string &append(const string &s, int pos, int n);
// 在当前字符串结尾添加n个字符c
string &append(int n, char c);

下面是字符串拼接的应用案例。

// string的拼接
void Test4()
{
string s1 = "abcd";
string s2 = "1111";
s1 += "abcd";
s1 += s2;
cout << s1 << endl; string s3 = "2222";
s2.append(s3);
cout << s2 << endl; string s4 = s2 + s3;
cout << s4 << endl;
}

string查找与替换

// 查找str第一次出现的位置,从Pos位置开始查找
int find(const string &str, int pos = 0) const;
// 查找s第一次出现的位置,从pos位置开始查找
int find(const char *s, int pos = 0) const;
// 从pos位置查找s的前n个字符第一次位置
int find(const char *s, int pos, int n) const;
// 查找字符c第一次出现的位置
int find(const char c, int pos = 0) const; // 从pos位置开始查找str最后一次出现的位置
int rfind(const string &str, int pos = npos) const;
// 从pos位置开始查找s最后一次出现的位置
int rfind(const char *s, int pos = npos) const;
// 从pos查找s的前n个字符最后一次位置
int rfind(const char *s, int pos, int n) const;
// 查找字符c最后一次出现的位置
int rfind(const char c, int pos = 0) const; // 替换从pos开始n个字符为字符串str
string &replace(int pos, int n, const string &str);
// 替换从pos开始的n个字符为字符串s
string &replace(int pos, int n, const char *s);

下面是字符串查找和替换的应用案例。

// 查找操作
void Test5()
{
string s1 = "abcdefghifgjklmn";
// 查找第一次出现的位置
int pos = s1.find("fg");
cout << "pos = " << pos << endl; // 查找最后一次出现的位置
int rpos = s1.rfind("fg");
cout << "rpos = " << rpos << endl;
} // string的替换
void Test6()
{
string s1 = "abcdefg";
s1.replace(0, 2, "111111");
cout << s1 << endl;
}

string比较

类似strcmp(),string提供了成员方法compare()用于进行string的比较

int compare(const string &s) const;
int compare(const char *s) const;

string子串

// 返回由pos开始的n个字符串组成的字符串
string substr(int pos = 0, int n = npos) const;

string插入和删除

// 插入字符串
string &insert(int pos, const char *s);
string &insert(int pos, const string &str);
// 在指定位置插入n个字符c
string &insert(int pos, int n, char c); // 删除从pos开始的n个字符
string &erase(int pos, int n = npos);

下面是string插入和删除的应用案例。

// string插入和删除
void Test7()
{
string s = "abcdefg";
s.insert(3, "111");
cout << s << endl; s.erase(3, 3);
cout << s << endl;
}

三 vector容器

3.1 vector动态增长原理

当插入新元素的时候,如果空间不足,那么vector会重新申请更大的一块内存空间,将原空间数据拷贝到新的空间,释放旧空间。再把新元素插入到新申请的空间。

3.2 vector构造函数

// 采用模板实现类实现,默认构造函数
vector<T> v; // 将v[begin(), end())区间中的元素作为初值进行初始化
vector(v.begin(), v.end()); // 将n个elem元素作为初值进行初始化
vector(n, elem); // 拷贝构造函数
vector(const vector &vec);

下面是vector初始化的应用案例

// 初始化
void Test1()
{
// 默认构造
vector<int> v1; int arr[] = { 10, 20, 30, 40, 50 };
vector<int> v2(arr, arr + sizeof(arr) / sizeof(int));
vector<int> v3(v2.begin(), v2.end());
vector<int> v4(v3); printVector(v2);
printVector(v3);
printVector(v4);
}

3.3 vector常用赋值操作

assign(beg, end);
assign(n, elem); // 重载等号运算符
vector &operator=(const vector &vec); // 将vec与本身的元素互换
swap(vec);

下面是赋值操作的应用案例

// 常用赋值操作
void Test2()
{
int arr[] = { 10, 20, 30, 40, 50 };
vector<int> v1(arr, arr + sizeof(arr) / sizeof(int)); // 成员方法进行赋值
vector<int> v2;
v2.assign(v1.begin(), v1.end()); // 重载=号赋值
vector<int> v3;
v3 = v2; // 将v1和v4的数据进行交换
vector<int> v4;
v4.push_back(100);
v4.push_back(200);
v4.push_back(300);
v4.swap(v1);
printVector(v1);
printVector(v4);
}

3.4 vector大小操作

// 返回容器中元素的个数
size() // 判断容器是否为空
empty(); // 重新制定容器长度为num,如果容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素将被删除
resize(int num); // 容器的容量
capacity(); // 容器预留len个元素长度,预留位置不初始化,元素不可访问
reserve(int len);
// 常用大小操作
void Test3()
{
int arr[] = { 100, 200, 300, 400 };
vector<int> v4(arr, arr + sizeof(arr) / sizeof(int)); cout << "v4.size() = " << v4.size() << endl;
if (v4.empty())
{
cout << "v4为空!" << endl;
} printVector(v4);
v4.resize(2);
printVector(v4);
// 设定填充值为1
v4.resize(6, 1);
printVector(v4); cout << "v4的容量:" << v4.capacity() << endl;
}

3.5 vector数据存取操作和插入删除

// 返回索引idx所指的数据,如果idx越界,则抛出out_of_range异常
at(int idx); // 返回索引idx所指的数据,越界时,运行直接报错
operator[] // 返回容器中的第一个数据元素
front(); // 返回容器中的最后一个数据元素
back();
// 往迭代器指向位置pos插入count个元素ele
insert(const_iterator pos, int count, T ele) // 尾部插入元素
push_back(); // 删除最后一个元素
pop_back(); // 删除迭代器从start到end之间的元素
erase(const_iterator start, const_iterator end); // 删除迭代器指向的元素
erase(const_iterator pos); // 清空容器中的所有元素
clear();

下面是vector的插入和删除的应用案例

// 插入和删除
void Test4()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
// 头插法
v.insert(v.begin(), 30);
v.insert(v.begin(), 40);
v.insert(v.end(), 50);
// vector支持随机访问,在位置v.begin()+2处插入60
v.insert(v.begin() + 2, 60);
printVector(v); v.erase(v.begin());
printVector(v);
v.pop_back();
printVector(v); v.clear();
cout << "v.size() = " << v.size() << endl;
}

3.6 使用swap()收缩空间

// swap()收缩空间
void Test5()
{
vector<int> v;
for (int i = 0; i < 100000; ++i)
v.push_back(i); cout << "v.size() = " << v.size() << endl;
cout << "v.capacity() = " << v.capacity() << endl; // swap()收缩空间,匿名对象随后会被释放空间
vector<int>(v).swap(v);
cout << "v.size() = " << v.size() << endl;
cout << "v.capacity() = " << v.capacity() << endl;
}

3.7 使用reserve()预留空间提高程序效率

reserve和resize有啥区别?

reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新对象之间,不能引用容器内的元素。resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了。

// reserve预留空间
void Test6()
{
int num = 0;
int *addr = nullptr;
vector<int> v; for (int i = 0; i < 100000; ++i)
{
v.push_back(i);
if (addr != &(v[0]))
{
++num;
addr = &(v[0]);
}
}
cout << "申请了" << num << "次内存!" << endl; // 如果你知道容器大概要存储多少内存空间,你可以使用reserve()预留空间
// 减少内存申请的次数以提高程序运行效率
num = 0;
addr = nullptr;
vector<int> u;
u.reserve(100000);
for (int i = 0; i < 100000; ++i)
{
u.push_back(i);
if (addr != &(u[0]))
{
++num;
addr = &(u[0]);
}
}
cout << "申请了" << num << "次内存!" << endl; }

C++ STL——string和vector的更多相关文章

  1. POJ 3096 Surprising Strings(STL map string set vector)

    题目:http://poj.org/problem?id=3096 题意:给定一个字符串S,从中找出所有有两个字符组成的子串,每当组成子串的字符之间隔着n字符时,如果没有相同的子串出现,则输出 &qu ...

  2. C++进阶 STL(1) 第一天 [容器,算法,迭代器] string容器 vector容器 deque容器

    课程大纲 02实现基本原理 容器,算法,迭代器 教室:容器 人:元素 教室对于楼:容器 序列式容器: 容器元素在容器中的位置是由进入容器的时间和地点来决定 序列式容器 关联式容器: 教室中 按年龄排座 ...

  3. STL容器之vector

    [1]模板类vector 模板类vector可理解为广义数组.广义数组,即与类型无关的数组,具有与数组相同的所有操作. 那么,你或许要问:既然C++语言本身已提供了一个序列式容器array,为什么还要 ...

  4. [知识点]C++中STL容器之vector

    零.STL目录 1.容器之map 2.容器之vector 3.容器之set 一.前言 关于STL和STL容器的概念参见STL系列第一篇——map(见上).今天介绍第二个成员——vector. 二.用途 ...

  5. std::string,std::vector,std::accumulate注意事项

    在用string做字符串拼接时,会发现随着string的增大越来越慢,原因主要是string(和vector)是基于现行内存的数据结构,在海量数据时,经常会申请新的一块内存,把原有的数据拷贝过去然后再 ...

  6. C++标准模板库Stand Template Library(STL)简介与STL string类

    参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...

  7. 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个对立的元素存于vector中

    #include<iostream> #include<string> #include<vector> #include<fstream> using ...

  8. 转:用STL中的vector动态开辟二维数组

    用STL中的vector动态开辟二维数组 源代码:#include <iostream>#include <vector>using namespace std;int mai ...

  9. stl string 使用指定的分隔符分割成数个子字符串

    #include <iostream> #include <vector> #include <string> #include <algorithm> ...

随机推荐

  1. ASE19团队项目alpha阶段model组 scrum4 记录

    本次会议于11月6日,19时整在微软北京西二号楼sky garden召开,持续50分钟. 与会人员:Jiyan He, Kun Yan, Lei Chai, Linfeng Qi, Xueqing W ...

  2. Python之IDE工具下载安装及注册详解及创建项目

    这篇文章很适合刚接触python语言的或者没有语言基础的同学参考: 目录: 一.IDE工具下载安装 二.IDE注册方法 三.使用IDE 开发工具使用创建项目 一.下载并安装, IntelliJ IDE ...

  3. 在VS2017添加MVC项目

    以前一直是用vs2012 MVC直接可以找到 在公司用的是vs2017  今天想创建一个MVC,和vs不同啊!!! 居然找不到MVC  然后找找找  ,突然想到创建asp.net 项目的时候发现了MV ...

  4. “System.Reflection.TargetInvocationException”类型的未经处理的异常在 mscorlib.dll 中发生

    异常汇总:http://www.cnblogs.com/dunitian/p/4523006.html#signalR 第一种,权限不够,在项目运行的时候弹出==>解决方法:以管理员权限运行vs ...

  5. 反编译DLL并修改DLL中的内容

    使用场景:针对当前用户因不愿意进行软件版本升级,但又希望可以解决当前问题,此时可以考虑通过反编辑DLL进行修改内容,然后重新生成新的DLL 操作步骤: 1.首先使用ILSpy.exe反编译DLL,查看 ...

  6. 【JOISC2012 / bzoj4388】Invitation

    Description link Solution 可以发现题目在模拟 Prim 算法求最大生成树的过程,树边故答案与起点 \(C\) 无关. 先把所有区间离散化,注意对于一个区间 \([l,r]\) ...

  7. jsp的标签库

    Java Server Pages Standard Tag Libray(JSTL):JSP 标准标签库,是一个定制标签类库的集合,用于解决一些常见的问题,例如迭代一个映射或者集合.条件测试.XML ...

  8. SqlMetaData异常 dbType xx 对于此构造函数无效。

    今天在dapper中想扩展使用表值类型参数——tableValue.但是dapper不支持此类参数,于是扩展了一下.其中出现了一个问题. Microsoft.SqlServer.Server.SqlM ...

  9. bind支持mysql

    最近打算将bind的记录信息存入到数据库中去,网上找了下,原来早有老外写好了mysql-bind的补丁,重新编译bind即可实现bind支持mysql存储.(http://mysql-bind.sou ...

  10. [Spring Boot] Set Context path for application in application.properties

    If you were using Microservice with Spring Boot to build different REST API endpoints, context path ...