1.string部分接口介绍
1.1 reserve && resize
#include <iostream>
#include <string>
using namespace std;
void test_string7()
{
string s;
// reverse 逆置
s.reserve(100); //保留 开空间
//s.resize(1000, 'x'); // 开空间 + 初始化
size_t sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
cout << "making s grow:\n";
for (int i = 0; i < 1000; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
int main()
{
test_string7();
return 0;
}
- reserve(100)开闭100个字节的空间,编译器会比我们想要开闭的空间更多一些
- 不同的平台,扩容机制也不同,有的是扩2倍,有的是扩1.5倍,
1.2 c_str
#include <iostream>
#include <assert.h>
using namespace std;
void test_string10()
{
string filename("test.cpp");
cout << filename << endl;
cout << filename.c_str() << endl;
filename += '\0';
filename += "string.cpp";
cout << filename << endl; // string 对象size为准
cout << filename.c_str() << endl; // 常量字符串对象\0
cout << filename.size() << endl;// 如果有\0,会算\0
cout << filename.length() << endl;// 不算f\0
string copy = filename;
cout << copy << endl << endl;
}
int main()
{
test_string10();
return 0;
}
- string容器默认不存储‘\0’
- string中的c_str会把string变成以‘\0’结尾的字符串
1.3 find && substr
#include <iostream>
#include <assert.h>
using namespace std;
void DealUrl(const string& url)
{
size_t pos1 = url.find("://");
if (pos1 == string::npos)
{
cout << "非法url" << endl;
return;
}
string protocol = url.substr(0, pos1);
cout << protocol << endl;
size_t pos2 = url.find('/', pos1 + 3);
if (pos2 == string::npos)
{
cout << "非法url" << endl;
return;
}
string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
cout << domain << endl;
string uri = url.substr(pos2 + 1);
cout << uri << endl << endl;
}
void test_string11()
{
string url1 = "https://cplusplus.com/reference/string/string/";
DealUrl(url1);
}
int main()
{
test_string11();
return 0;
}
- find 和 substr都是左闭右开的
- find 和 rfind都是从左向右看下标
1.4 compare
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("abcabc");
string s2("cab");
const char* s3 = "abc";
const char* s4 = "aaaa";
if (s1.compare(s2) == 0) {
cout << s1 << "=" << s2 << endl;
}
if (s1.compare(3, 3, s3) == 0) {
cout << s1 << "=" << s3 << endl;
//abcabc
// abc
}
return 0;
}
1.5 append拼接
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("I");
string s2(" like");
//append(string)完成两个string对象的拼接
s1.append(s2); //I like
//append(str)完成string对象和字符串str的拼接
s1.append(" C++"); //I like C++
//append(n, char)将n个字符char拼接到string对象后面
s1.append(3, '!'); //I like C++!!!
cout << s1 << endl; //I like C++!!!
return 0;
}
- 相当于尾插字符串
1.6 to_string && stoi && stod
#include <iostream>
#include <string>
using namespace std;
void test_string12()
{
int ival;
double dval;
cin >> ival >> dval;
string istr = to_string(ival);
string dstr = to_string(dval);
cout << istr << endl;
cout << dstr << endl;
istr = "9999";
dstr = "9999.99";
ival = stoi(istr);
dval = stod(dstr);
cout << typeid(ival).name() << endl;
cout << typeid(dval).name() << endl;
}
int main()
{
test_string12();
return 0;
}
- to_string:其他类型转换成字符
- stoi:字符转化成整型
- stod:字符转化成浮点型
1.7 clear
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s("CSDN");
//clear()删除对象的内容,该对象将变为空字符串
s.clear();
cout << s << endl; //空字符串
return 0;
}
- 使string变成空字符串
1.8 模拟实现string
namespace test
{
//模拟实现string类
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
//默认成员函数
string(const char* str = ""); //构造函数
string(const string& s); //拷贝构造函数
string& operator=(const string& s); //赋值运算符重载函数
~string(); //析构函数
//迭代器相关函数
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
//容量和大小相关函数
size_t size();
size_t capacity();
void reserve(size_t n);
void resize(size_t n, char ch = '\0');
bool empty()const;
//修改字符串相关函数
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len = npos);
void clear();
void swap(string& s);
const char* c_str()const;
//访问字符串相关函数
char& operator[](size_t i);
const char& operator[](size_t i)const;
size_t find(char ch, size_t pos = 0)const;
size_t find(const char* str, size_t pos = 0)const;
size_t rfind(char ch, size_t pos = npos)const;
size_t rfind(const char* str, size_t pos = 0)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;
bool operator!=(const string& s)const;
private:
char* _str; //存储字符串
size_t _size; //记录字符串当前的有效长度
size_t _capacity; //记录字符串当前的容量
static const size_t npos; //静态成员变量(整型最大值)
};
const size_t string::npos = -1;
// <<和>>运算符重载函数
istream& operator>>(istream& in, string& s);
ostream& operator<<(ostream& out, const string& s);
istream& getline(istream& in, string& s);
}
- 参考文章: string类的模拟实现
2. vector部分接口介绍
2.1 reserve && resize
#include <iostream>
#include <vector>
namespace std
{
void TestVectorExpand()
{
size_t sz;
vector<int> v;
//v.resize(100);
v.reserve(100);
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 1000; ++i)
{
v.push_back(i);
if (sz != v.capacity())
{
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
void test_vector3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
cout << v1.max_size() << endl;
TestVectorExpand();
}
}
int main()
{
std::test_vector3();
return 0;
}
- 空间只能变大或者不变
- reserve开空间,它是期待预留空间,
- 如果原来空间大,期待预留空间小,则空间不变
- 如果原来空间小,期待预留空间大,则空间变大
- resize开空间和初始化,它是调整元素个数,
- 如果调整之后元素个数比空间小,则空间不变,size会变小
- 如果调整之后元素个数比空间大,则空间变大,size会变大
- max_size返回的是\容纳最大元素的数
2.2 insert && erase
#include <iostream>
#include <vector>
#include <algorithm>
namespace std
{
void test_vector4()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);
// 如果find没找到,就会返回vector最后一个元素的下一个,即end(),
if (pos != v1.end())
{
v1.insert(pos, 30);
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
pos = find(v1.begin(), v1.end(), 30);
if (pos != v1.end())
{
v1.erase(pos);
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
}
int main()
{
std::test_vector4();
return 0;
}
- vector中没有实现find接口,但algorithm中定义了find函数,还有sort函数
- find的返回值的类型是迭代器,不是size_t,且如果find没找到,就会返回vector最后一个元素的下一个,即end(),
3. vector中迭代器失效问题
3.1 问题一:pos指向位置已经不再是原来的值了(因为数据挪动)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
// 在所有的偶数前面插入这个偶数的2倍
auto it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
it = v1.insert(it, *it * 2);
}
it++;
}
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
- insert返回的是原来的位置,新的值
- 这段代码会直接崩溃,这不仅insert会产生上面的问题,erase也可能会有这样的问题
解决方案
- 只要让它插入值之后,多走一步就行了
问题原因
- 这是在vs2022中调式的结果
3.2 问题二:扩容导致野指针问题
- 我这里用vs2022不好演示,只要涉及扩容的操作,都有可能产生野指针问题,比如insert
3.3 对迭代器失效的补充说明
- STL只是一个规范,不同的平台下面实现也是不同的,
所以相同的代码,产生的结果不一样,也是正常的 - vs的编译器采用的是PJ版
- linux的g++编译器采用的是SG版
- 数据排列偶然性:a.最后一个不是偶数,b.没有连续的偶数,
3.4 小结
- insert/erase pos位置,不要再次直接访问pos,可能出现各种出乎意料的结果,这就是所谓的迭代器失效
3.5 模拟实现vector
namespace test
{
//模拟实现vector
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
//默认成员函数
vector(); //构造函数
vector(size_t n, const T& val); //构造函数
template<class InputIterator>
vector(InputIterator first, InputIterator last); //构造函数
vector(const vector<T>& v); //拷贝构造函数
vector<T>& operator=(const vector<T>& v); //赋值运算符重载函数
~vector(); //析构函数
//迭代器相关函数
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
//容量和大小相关函数
size_t size()const;
size_t capacity()const;
void reserve(size_t n);
void resize(size_t n, const T& val = T());
bool empty()const;
//修改容器内容相关函数
void push_back(const T& x);
void pop_back();
void insert(iterator pos, const T& x);
iterator erase(iterator pos);
void swap(vector<T>& v);
//访问容器相关函数
T& operator[](size_t i);
const T& operator[](size_t i)const;
private:
iterator _start; //指向容器的头
iterator _finish; //指向有效数据的尾
iterator _endofstorage; //指向容器的尾
};
}
- 参考文档: vector的模拟实现
4. list部分接口介绍
4.1 sort
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <time.h>
using namespace std;
void test_op()
{
srand(time(0));
const int N = 10000000;
vector<int> v;
v.reserve(N);
list<int> lt1;
list<int> lt2;
for (int i = 0; i < N; ++i)
{
auto e = rand();
lt1.push_back(e);
lt2.push_back(e);
}
// 拷贝到vector排序,排完以后再拷贝回来
int begin1 = clock();
for (auto e : lt1)
{
v.push_back(e);
}
sort(v.begin(), v.end());// 调用库里面的sort
size_t i = 0;
for (auto& e : lt1)
{
e = v[i++];// 拷贝回去
}
int end1 = clock();
int begin2 = clock();
// sort(lt.begin(), lt.end()); error
lt2.sort();// 调用list自己的sort
int end2 = clock();
printf("copy vector sort:%d\n", end1 - begin1);
printf("list sort:%d\n", end2 - begin2);
}
int main()
{
test_op();
return 0;
}
- 这里把list数据拷贝给vector,vector排序之后又拷贝给list
- 这里是调用list自己的sort排序(不能调用库里面的sort)
4.2 list的模拟实现
namespace cl
{
//模拟实现list当中的结点类
template<class T>
struct _list_node
{
//成员函数
_list_node(const T& val = T()); //构造函数
//成员变量
T _val; //数据域
_list_node<T>* _next; //后继指针
_list_node<T>* _prev; //前驱指针
};
//模拟实现list迭代器
template<class T, class Ref, class Ptr>
struct _list_iterator
{
typedef _list_node<T> node;
typedef _list_iterator<T, Ref, Ptr> self;
_list_iterator(node* pnode); //构造函数
//各种运算符重载函数
self operator++();
self operator--();
self operator++(int);
self operator--(int);
bool operator==(const self& s) const;
bool operator!=(const self& s) const;
Ref operator*();
Ptr operator->();
//成员变量
node* _pnode; //一个指向结点的指针
};
//模拟实现list
template<class T>
class list
{
public:
typedef _list_node<T> node;
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
//默认成员函数
list();
list(const list<T>& lt);
list<T>& operator=(const list<T>& lt);
~list();
//迭代器相关函数
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
//访问容器相关函数
T& front();
T& back();
const T& front() const;
const T& back() const;
//插入、删除函数
void insert(iterator pos, const T& x);
iterator erase(iterator pos);
void push_back(const T& x);
void pop_back();
void push_front(const T& x);
void pop_front();
//其他函数
size_t size() const;
void resize(size_t n, const T& val = T());
void clear();
bool empty() const;
void swap(list<T>& lt);
private:
node* _head; //指向链表头结点的指针
};
}
- 参考代码: list的模拟实现
5. 理解 stack && queue
- 栈和队列的模拟实现都是通过复用来实现的
- 栈的适配器Container可以是vector或者是list,队列的适配器Container可以是list
- 库里面提供了适配器,使得栈和队列的底层有了更大的发挥空间,更像泛型编程
5.1 stack的模拟实现
namespace lyc //防止命名冲突
{
template<class T, class Container = std::deque<T>>
class stack
{
public:
//元素入栈
void push(const T& x)
{
_con.push_back(x);
}
//元素出栈
void pop()
{
_con.pop_back();
}
//获取栈顶元素
T& top()
{
return _con.back();
}
const T& top() const
{
return _con.back();
}
//获取栈中有效元素个数
size_t size() const
{
return _con.size();
}
//判断栈是否为空
bool empty() const
{
return _con.empty();
}
//交换两个栈中的数据
void swap(stack<T, Container>& st)
{
_con.swap(st._con);
}
private:
Container _con;
};
}
5.2 queue的模拟实现
namespace lyc //防止命名冲突
{
template<class T, class Container = std::deque<T>>
class queue
{
public:
//队尾入队列
void push(const T& x)
{
_con.push_back(x);
}
//队头出队列
void pop()
{
_con.pop_front();
}
//获取队头元素
T& front()
{
return _con.front();
}
const T& front() const
{
return _con.front();
}
//获取队尾元素
T& back()
{
return _con.back();
}
const T& back() const
{
return _con.back();
}
//获取队列中有效元素个数
size_t size() const
{
return _con.size();
}
//判断队列是否为空
bool empty() const
{
return _con.empty();
}
//交换两个队列中的数据
void swap(queue<T, Container>& q)
{
_con.swap(q._con);
}
private:
Container _con;
};
}
6. 关于适配器Container的缺省参数deque
- deque就像是list和vector的合体,不仅支持头删头插,尾删尾删,还支持随机访问
- 在deque的底层中,是先开一段空间,空间不够再开一段空间(这里没有扩容),
6.1 deque的优势和缺陷
- 优势: a.头尾插入删除,b.随机访问
- 缺陷:
- operator[]计算稍显复杂,大量使用,将会导致性能下降(与vector的[]相比)
- 中间插入删除效率不高
- 迭代器会很复杂
头尾的插入和删除都非常适合,相比vector和list而言,所以很适合去做stack和queue的默认适配器
7. priority_queue(优先级队列)
priority_queue<int, vector<int>, less<int>> q1;
7.1 堆的向上调整算法
#include <iostream>
#include <functional>
#include <queue>
using namespace std;
int main()
{
priority_queue<int> q;
q.push(3);
q.push(6);
q.push(0);
q.push(2);
q.push(9);
q.push(8);
q.push(1);
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
cout << endl; //9 8 6 3 2 1 0
return 0;
}
7.2 堆的向下调整算法
以大堆为例,使用堆的向下调整算法有一个前提,就是待向下调整的结点的左子树和右子树必须都为大堆。
//堆的向下调整(大堆)
void AdjustDown(vector<int>& v, int n, int parent)
{
//child记录左右孩子中值较大的孩子的下标
int child = 2 * parent + 1;//先默认其左孩子的值较大
while (child < n)
{
if (child + 1 < n&&v[child] < v[child + 1])//右孩子存在并且右孩子比左孩子还大
{
child++;//较大的孩子改为右孩子
}
if (v[parent] < v[child])//左右孩子中较大孩子的值比父结点还大
{
//将父结点与较小的子结点交换
swap(v[child], v[parent]);
//继续向下进行调整
parent = child;
child = 2 * parent + 1;
}
else//已成堆
{
break;
}
}
}
7.3 priorty_queue的模拟实现
namespace lyc //防止命名冲突
{
//比较方式(使内部结构为大堆)
template<class T>
struct less
{
};
//比较方式(使内部结构为小堆)
template<class T>
struct greater
{
};
//优先级队列的模拟实现
template<class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue
{
public:
//堆的向上调整
void AdjustUp(int child)
{
}
//插入元素到队尾(并排序)
void push(const T& x)
{
}
//堆的向下调整
void AdjustDown(int n, int parent)
{
}
//弹出队头元素(堆顶元素)
void pop()
{
}
//访问队头元素(堆顶元素)
T& top()
{
}
const T& top() const
{
}
//获取队列中有效元素个数
size_t size() const
{
}
//判断队列是否为空
bool empty() const
{
}
private:
Container _con; //底层容器
Compare _comp; //比较方式
};
}
- 相关代码: priority_queue模拟实现
- priority_queue的底层是用数据结构中的堆实现,
- 不传第三个模板参数(仿函数)默认是大堆
8. 仿函数
#include <iostream>
#include <algorithm>
#include <queue>
#include <functional>
using namespace std;
/*仿函数/函数对象 -- 类,重载operator()
类对象可以像函数一样去使用*/
namespace bit
{
template<class T>
class less
{
public:
bool operator()(const T& l, const T& r) const
{
return l < r;
}
};
template<class T>
class greater
{
public:
bool operator()(const T& l, const T& r) const
{
return l > r;
}
};
}
int main()
{
bit::less<int> lsFunc;
cout << lsFunc(1, 2) << endl;
// 等价于下面
//cout << lsFunc.operator()(1, 2) << endl;
bit::greater<int> gtFunc;
cout << gtFunc(1, 2) << endl;
// 等价于下面
//cout << lsFunc.operator()(1, 2) << endl;
return 0;
}
-
仿函数就是一个类重载了()运算符,
8.1 比较sort的仿函数和priority_queue的仿函数
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
int main()
{
int arr[] = { 0,1,2,3,4,5,6 };
vector<int> v(arr,arr+sizeof(arr)/sizeof(arr[0]));
sort(v.begin(), v.end(), greater<int>());// 不传第三个参数默认排升序
// sort(v.begin(), v.end());// 不传第三个参数默认排升序
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
priority_queue<int,vector<int>,greater<int>> pq;// 不传第三个参数模板参数默认排大堆
pq.push(4);
pq.push(3);
pq.push(2);
pq.push(1);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
return 0;
}
- 不同点:sort是传的仿函数对象,priority是传的仿函数类型
9. 容器的分类
- forward_iterator 单向迭代器
- bidirectional_iterator 双向迭代器
- random_access_iterator 随机迭代器
今天的文章sta接口_stl编程指令详解「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/64798.html