[C++ 面试基础知识总结] 顺序容器

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: [C++ 面试基础知识总结] 顺序容器参考书籍:《C++ Primer》目录C 面试基础知识总结 顺序容器目录顺序容器与选择迭代器容器的初始化和赋值顺序容器操作添加元素访问元素删除元素改变容器大小迭代器失效vector对象的增长string 操作改变string搜索string数值转换容器适配器栈sta

[C++ 面试基础知识总结] 顺序容器

参考书籍:《C++ Primer》

目录

顺序容器与选择

顺序容器类型:
vector 可变大小数组
deque 双端队列
list 双向链表
forward_list 单向链表
array 固定大小数组
string 专门用于保存字符的可变大小数组

选择容器的基本原则:
1.默认情况用vector
2.小元素多,空间额外开销重要的时候避免使用list或forward_list
3.要求随机访问元素时,用vector或deque
4.要求在容器中间插入或删除元素时,用list或forward_list
5.需要在头尾插入或删除元素,但不会在中间进行次操作时,用deque

复合使用:如果一个程序只有在读取输入时才需要在容器中间插入元素,随后需要随机访问元素,则可以在输入阶段使用list,一旦输入完成后,将list中的内容拷贝到一个vector中。

迭代器

迭代器的基本操作可以参考:[C++ 面试基础知识总结]字符串,向量和数组

begin和end操作生成指向容器中第一个元素和尾元素之后位置的迭代器。除forward_list以外的顺序容器还支持按逆序寻址元素的迭代器reverse_iterator。采用rbegin和rend操作分别生成指向容器尾元素和收元素之前位置的迭代器。

    list<string> a ;
    auto i1 = a.begin();   //list<string>::iterator
    auto i2 = a.rbegin();  //list<string>::reverse_iterator
    auto i3 = a.cbegin();  //list<string>::const_iterator
    auto i4 = a.crbegin(); //list<string>::const_reverse_iterator

容器的初始化和赋值

创建一个容器为另一个容器的拷贝时,两个容器的类型和其存放的元素类型都必须匹配。用传递迭代器参数来拷贝一个范围时,就不要求容器类型是相同的了,容器中的元素类型也可以不同,只要是可以转换的即可。

    list<int> l(10,0);
    //拷贝容器
    list<int> l2(l);
    //采用迭代器拷贝范围
    vector<double> v (l.begin(),l.end());

array的大小也是类型的一部分,当定义一个array时,除了指定元素类型,还要指定容器大小。

    //正确,保存10个int的数组
    array<int,10> a1;
    //错误,array<int>不是一个容器
    array<int> a2;

进行赋值运算时,如果两个容器原来大小不等,则让两个容器的大小都与右边容器的原大小相等。

    list<int> l(10,1);
    list<int> l2(5,-1);
    l = l2;
    //l的大小为5,即l2的大小
    cout << l.size() << endl;

除array以外的顺序容器都支持assign,assign可以将右边运算对象的元素拷贝到左边运算对象中,也可以指定数目且具有相同给定值的元素替换容器中的原有元素。

    list<string> l;
    vector<const char *> v;
    //将v中的元素替换为该范围内的元素
    v.assign(l.begin,l.end);
    //将v中的元素替换为5个0
    v.assign(5,0);

顺序容器操作

添加元素

using namespace std;

struct People{
    People(const string &name, int age): name(name),age(age){}
    string name;
    int age;
};

int main(int argc, const char * argv[]) {
    list<People> l;
    vector<People> v;

    People p("Summer",26);

    l.push_front(p);   //在容器头部添加元素 vector不支持
    v.push_back(p);   //在容器尾部添加元素 forward_list不支持
    v.emplace_back("Summer",26);   //采用构造函数的方法在容器尾部添加元素
    v.insert(v.begin(),p);  //在指定位置(v的首元素)之前插入元素,vector不推荐在除尾部以外的位置插入元素
    v.insert(v.begin(),l.begin(),l.end()); //在指定位置之前(v的首元素)之前插入l的所有元素
    v.insert(v.begin(),v.begin(),v.end());  //错误,迭代器表示拷贝范围,不能指向与目标相同的容器

    return 0;
}

注意:使用一个对象来初始化容器或将一个对象插入到容器时,实际上放入容器中的是对象值的一个拷贝,而不是对象本身。容器中的元素与提供值得对象之间没有任何关联!

访问元素

访问容器中的元素的主要方法:解引迭代器,调用front,back函数,使用下标。

    vector<int> v(10,0);

    // 以下4种写法都是访问v首元素
    auto p1 = *v.begin();
    auto p2 = v.front();
    auto p3 = v[0];
    auto p4 = v.at(0);

注意:如果容器为空,调用front,back函数会发生严重错误。在使用下标时需要保证给定下标在有效范围内,防止发生越界。在容器中访问成员函数返回的都是引用,如果容器是一个const对象,则返回值是const引用。如果容器不是const,则可以用返回值来改变元素的值。

删除元素

    list<int> l(10,0);
    vector<int> v(10,0);

    l.pop_front();  // 删除首元素,vector不支持
    v.pop_back();  //删除尾元素,forward_list不支持
    v.erase(v.begin);  // 删除指定位置的元素,返回指向被删元素之后的迭代器
    v.erase(v.begin,v.end); // 删除指定区域内的元素,返回指向最后一个被删元素之后的迭代器,这里等价于v.clear()
    v.clear(); // 删除所有元素

注意:删除元素的函数都不会检查其参数,所以在删除元素之前,必须保证被删元素确实存在。

改变容器大小

改变容器大小时,如果容器缩小,容器后部的元素会被删除,如果容器增大,会在容器后部添加新元素。

    vector<int> v(10,1);

    v.resize(15); //将5个值为0的元素添加到vector末尾
    v.resize(20,-1); //将5个值为-1的元素添加到vector末尾
    v.resize(5); //从vector末尾删除15个元素

迭代器失效

向容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的指针,引用或迭代器失效。使用失效的指针,引用或迭代器是一种严重错误。当使用迭代器时,最好保证在每次改变容器的操作之后都正确地重新定位迭代器。
注意:end返回的迭代器很容易失效,不要保存end返回的迭代器。

vector对象的增长

int main(int argc, const char * argv[]) {
    vector<int> v(50,1);

    v.reserve(60);  //为v分配能容纳60个元素的内存空间
    cout << v.size() << endl;  //v的大小为50
    cout << v.capacity() << endl;  //v的容量为60

    v.shrink_to_fit();  //将v的容量减小到与大小相当
    cout << v.size() << endl;  //v的大小为50
    cout << v.capacity() << endl;  //v的容量为50

    v.push_back(1);  //新增一个元素,v的容量已满,系统为v重新分配内存空间
    cout << v.size() << endl;  //v的大小为51
    cout << v.capacity() << endl;  //v的容量为100,系统自动为v重新分配的内存空间为原来的2倍
    return 0;
}

在有些编程环境下运行最后一条语句,输出的v的容量也有可能是75,即系统自动为v重新分配的内存空间为原来的1.5倍。

string 操作

string除了支持顺序容器的通用操作外,还支持一些其他的操作。

改变string

    string s = "Summer love C++";
    s = s.substr(0,11);  // 从下标为0的位置开始的11个字符,s = "Summer love"
    s = s.append(" Swift"); // 在尾部添加" Swift",s = "Summer love Swift"
    s = s.replace(12, 5, "Objective-C"); // 将从下标为12位置开始的5个字符替换为"Objective-C",s = "Summer love Objective-C"

注意:substr和replace函数的开始位置不能超出string的长度,操作长度最大值为string长度与开始位置之差。

搜索string

    string s = "Summer love C++";
    auto pos = s.find("love");  //pos7,查找"love"第一次出现的位置
    pos = s.find_first_of("love");  //pos4,查找任何"love"中的字符第一次出现的位置
    pos = s.find_first_of("love",6);  //pos7,从位置6开始查找任何"love"中的字符第一次出现的位置
    pos = s.find_first_not_of("love");  //pos0,查找任何非"love"中的字符第一次出现的位置
    pos = s.find_last_of("love");  //pos10,查找任何"love"中的字符最后一次出现的位置

注意:搜索对大小写敏感!

数值转换

    float pi = 3.14;
    string s = to_string(pi); //数值转化为字符串

    int i = stoi(s);  //字符串转化整数
    long l = stol(s);  //字符串转化长整数
    unsigned long ul = stoul(s);  //字符串转化无符号长整数
    float f = stof(s);  //字符串转化浮点数
    double d = stod(s)  //字符串转化无符号浮点数

容器适配器

除了顺序容器外,标准库还定义了3个顺序容器适配器:stack,queue和priority_queue。适配器可以接受一种已有的容器类型,使其行为看起来像一种不同的类型。所有适配器都要求容器具有添加,删除元素和访问尾元素的能力。所以array和forward_list无法用来构造适配器。

栈stack

stack只需要实现添加,删除,访问尾元素操作,所以可以用vector,list,deque来构造,默认情况下stack是基于deque构造的。

    deque<int> d;
    vector<int> v;

    stack<int> s1(d); //基于deque构造栈,默认情况
    stack<int,vector<int>> s2(v); //基于vector构造栈,需要在第二个参数传入容器类型
    stack<int> s3;  //不用顺序容器,直接构造栈

栈的基本操作

    vector<int> v = {1,2,3,4,5};
    stack<int,vector<int>> s(v); // 用vector构造栈
    s.push(6); // 将一个新元素压入栈顶
    int i = s.top();  //返回栈顶元素 i=6
    s.pop();  //删除栈顶元素(出栈)
    i = s.top();  //返回栈顶元素 i=5

队列queue

queue需要实现添加尾元素,删除首元素,访问首尾元素操作,所以只能用list,deque来构造,默认情况下queue是基于deque构造的。

    deque<int> d;
    list<int> l;

    queue<int> q1(d); //基于deque构造队列,默认情况
    queue<int,list<int>> q2(l);  //基于list构造栈,需要在第二个参数传入容器类型
    queue<int> q3; //不用顺序容器,直接构造队列

队列的基本操作

    list<int> l = {1,2,3,4,5};
    queue<int,list<int>> q(l); // 用list构造队列

    int front = q.front(); //返回队首元素,front=1
    q.pop();  //删除队首元素(出队)
    front = q.front(); //返回队首元素,front=2

    int back = q.back();  //返回队尾元素,front=5
    q.push(6);  //将6添加到队尾(入队)
    back = q.back();  //返回队尾元素,front=6

priority_queue与queue的区别在于priority_queue,需要实现的是队尾元素的添加删除和随机访问,所以只能用vector和deque进行构造,默认情况是用vector构造priority_queue。且priority_queue的元素是按优先级排序的(queue是按入队顺序),可用q.top()返回优先级最高的元素。

目录
相关文章
|
3天前
|
存储 C++ 容器
C++标准库中提供了哪些数据容器作为数组的替代
C++标准库中提供了哪些数据容器作为数组的替代
15 5
|
10天前
|
安全 程序员 C++
C++一分钟之-C++中的并发容器
【7月更文挑战第17天】C++11引入并发容器,如`std::shared_mutex`、`std::atomic`和线程安全的集合,以解决多线程中的数据竞争和死锁。常见问题包括原子操作的误用、锁的不当使用和迭代器失效。避免陷阱的关键在于正确使用原子操作、一致的锁管理以及处理迭代器失效。通过示例展示了如何安全地使用这些工具来提升并发编程的安全性和效率。
14 1
|
24天前
|
C语言 C++ 开发者
C++基础知识(一:命名空间的各种使用方法)
C++在C的基础上引入了更多的元素,例如类,类的私密性要比C中的结构体更加优秀,引用,重载,命名空间,以及STL库,模板编程和更多的函数,在面向对象的编程上更加高效。C语言的优势则是更加底层,编译速度会更快,在编写内核时大多数都是C语言去写。 在C++中,命名空间(Namespace)是一种组织代码的方式,主要用于解决全局变量、函数或类的命名冲突问题。命名空间提供了一种封装机制,允许开发者将相关的类、函数、变量等放在一个逻辑上封闭的区域中,这样相同的名字在不同的命名空间中可以共存,而不会相互干扰。
|
16天前
|
Java 应用服务中间件 持续交付
Java面试题:简述Docker等容器化技术的原理及其在Java应用部署中的作用。
Java面试题:简述Docker等容器化技术的原理及其在Java应用部署中的作用。
26 0
|
24天前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。
|
24天前
|
算法 编译器 C++
C++基础知识(三:哑元和内联函数和函数重载)
在C++编程中,"哑元"这个术语虽然不常用,但可以理解为在函数定义或调用中使用的没有实际功能、仅作为占位符的参数。这种做法多见于模板编程或者为了匹配函数签名等场景。例如,在实现某些通用算法时,可能需要一个特定数量的参数来满足编译器要求,即使在特定情况下某些参数并不参与计算,这些参数就可以被视为哑元。
|
24天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
24天前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。
|
24天前
|
存储 编译器 C++
C++基础知识(六:继承)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
24天前
|
存储 编译器 C++
C++基础知识(七:多态)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。