【C++】STL--vector容器(构造、遍历、插入删除、容量大小、数据存取、动态扩容等)

vector

前言

这几天在写代码的时候用到STL较多,也感受到了STL的强大,于是我重新温习了一遍STL的内容,并把一些基础的东西,重要的内容总结提炼出来,这篇是vector篇。C++的STL给我们提供了很多可用的容器,vector就是最常用的容器之一,vector应该掌握的基础内容包括构造函数、元素的插入插入删除、赋值、存取等操作。重点内容应该掌握vector的扩容机制、内存大小的变化原理等。

1、vector简介

  • vector容器为可变长数组(动态数组),可以随时插入删除元素。
  • vector被称为向量容器,因为该容器擅长在尾部插入或删除元素, 在O(1)时间就能够完成;而如果要在容器头部或者中间插入数组元素,则需花费O(N)的时间,因为他需要将后面的元素往后移。(其实原理也就类似于我们在数据结构与算法中学的顺序表)
  • vector与普通数组大的区别在于:数组是静态数组(容量固定的数组),而vector可以动态拓展。即可以进行容器的插入和删除,这个过程中,vector会动态调整所占用的空间。
  • vector访问元素也同样要遵守不能越界的规则,否则会造成内存的安全隐患。

2、构造函数

(1)功能:用来创建vector容器

(2)方式:

cpp 复制代码
1. vector<T> v;//默认无参构造
2. vector<T> v{elem1, elem2, elem3 ...}//指定元素构造
3. vector<T> v(n, elem)//将n个elem拷贝为元素构造
4. vector<T> v2(v1)//拷贝另一个vector构造
5. vector<T> v2(v1.begin(), v1.end())//区间构造,将v1区间的begin()到end()的元素赋值给v2;

//二维构造初始化
vector<vector<int>> v;
vector<vector<int>> v(n + 1, vector<int>(m + 1, 0));//二维构造

示例:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

//打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

//vector容器构造
void test01() {
    vector<int> v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);

    vector<int> v2{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    printVector(v2);

    vector<int> v3(v1.begin(), v1.end());
    printVector(v3);

    vector<int> v4(11, 4);
    printVector(v4);

    vector<int> v5(v4);
    printVector(v4);
}


int main() {
    test01();
    return 0;
}

运行结果:

3、赋值操作

(1)功能:给vector容器赋值

(2)方式:

cpp 复制代码
1. vector& operator = (const vector &vec);//重载等号运算符
2. v.assign(v1.begin(), v1.end())用assign来区间赋值
3. v.assign(n, elem),将n个elem拷贝赋值给元素

示例:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

//打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

void test02() {
    vector <int> v1;
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i);
    }
    printVector(v1);

    vector<int> v2;
    v2 = v1;
    printVector(v2);

    vector<int> v3;
    v3.assign(v1.begin(), v1.end());
    printVector(v3);

    vector<int> v4;
    v4.assign(11, 4);
    printVector(v4);
}

int main() {
    test02();
    return 0;
}

运行结果:

4、容量和大小

(1)功能:对vector容器进行容量和大小的操作

(2)方式:

cpp 复制代码
1. v.empty();//用来判空
2. v.capacity();//用来获取容器中元素个数的大小
3. v.size();//用来获取容器当前元素个数
4. v.resize(int num);//用来重新指定容器的长度,如果变短则阶段删除
5. v.resize(int num, int elem);//用来重新指定容器的长度,并以elem来填充

示例:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

//打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

//容量、大小
void test03() {
    vector<int> v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);

    if (v1.empty()) {
        cout << "v1为空" << endl;
    }
    else {
        cout << "v1不为空" << endl;
        cout << "capacity容量:" << v1.capacity() << endl;
        cout << "v1的大小为:" << v1.size() << endl;

        //重新指定大小
        v1.resize(14);  //如果重新指定的比原来长了,默认用0填充新的位置
        printVector(v1);

        v1.resize(11, 45);  //重载,参数2可以指定默认填充值
        printVector(v1);

        v1.resize(4);  //如果重新指定的比原来短了,超出的部分会被截断
        printVector(v1);
    }
}

int main() {
    test03();
    return 0;
}

运行结果:

补充:

  • vector中的capacity指的是容量,即在不分配更多内存的情况下,可以保存的最多个数,而size则是当前容器的大小。
  • size 总是小于或等于 capacity
  • 当容器中的size == capacity时,此时再向其中插入内容的话,则vector会申请出更多的空间。
  • 申请空间时不是直接在原来的空间上连续往下开辟,而是另找一块更大的空间,完成拷贝操作后,然后把当前vector的指针指向新的更大的空间。

5、插入和删除

(1)功能:往vector容器中插入和删除元素 (2)方式:

cpp 复制代码
1. v.push_back(ele);//尾插元素
2. v.pop_back();//删除最后一个元素
3. v.insert(const_iterator pos, ele);//在迭代器指向位置pos处插入元素ele
4. v.insert(const_iterator  pos, count, ele);//在指定位置pos插入count个元素
5. v.erase(const_iterator  pos);//删除迭代器指向位置的元素
6. v.erase(const_iterator  start, const_iterator end);//删除迭代器从start到end之间的元素
7. v.clear();//删除容器中所有的元素

示例:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

//打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

//插入和删除
void test04() {
    vector<int> v1;
    //尾插
    v1.push_back(11);
    v1.push_back(4);
    v1.push_back(5);
    v1.push_back(14);
    printVector(v1);

    //尾删
    v1.pop_back();
    printVector(v1);

    //指定位置插入
    v1.insert(v1.begin(), 114);
    printVector(v1);

    //指定位置指定数量插入
    v1.insert(v1.begin(), 2, 514);
    printVector(v1);

    //删除
    v1.erase(v1.begin());
    printVector(v1);

    vector<int> v2(v1);

    //删除区间
    v1.erase(v1.begin(), v1.end());
    printVector(v1);

    cout << "v2清空前:";
    printVector(v2);
    //清空
    v2.clear();
    cout << "v2清空后: ";
    printVector(v1);
}

int main() {

    test04();
    return 0;
}

运行结果:

6、数据存取操作

(1)功能:对vector中的数据进行存储和拿取操作。

(2)方式:

cpp 复制代码
1. operator[index];//类似数组中的[],返回索引index所指向的数据
2. at(int index);//用at函数来返回索引index所指向的数据
3. front();//返回容器中的第一个数据
4. back();//返回容器中最后一个元素

示例:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

//打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

void test05() {
    vector<int> v1;
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i);
    }

    //用[]的方式访问数组中的元素
    for (int i = 0; i < v1.size(); ++i) {
        cout << v1[i] << " ";
    }
    cout << endl;

    //用at的方式访问数组中的元素
    for (int i = 0; i < v1.size(); ++i) {
        cout << v1.at(i) << " ";
    }
    cout << endl;

    //获取第一个元素
    cout << "第一个元素为:" << v1.front() << endl;

    //获取最后一个元素
    cout << "最后一个元素为: " << v1.back() << endl;
}

int main() {
    test05();
    return 0;
}

运行结果:

7、互换容器

(1)功能:将两个容器的元素进行交换 (2)方式:

cpp 复制代码
v1.swap(v2);//将v2的元素与v1元素互换

示例:

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

//打印vector
void printVector(vector<int>& v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

void test06() {
    cout << "交换前:" << endl;

    vector<int> v1;
    for (int i = 0; i < 10; ++i) {
        v1.push_back(i);
    }
    printVector(v1);

    vector<int> v2;
    for (int i = 10; i > 0; i--) {
        v2.push_back(i);
    }
    printVector(v2);

    cout << "交换后:" << endl;
    v1.swap(v2);
    printVector(v1);
    printVector(v2);
}

int main() {
    test06();
    return 0;
}

运行结果:

补充(重要用途):

我们可以使用swap来使两个容器互换,加之匿名对象的性质,可以用来达到收缩内存空间的目的。方式如下:

cpp 复制代码
void test07() {
    vector<int> v;
    for(int i = 0; i < 114514; i++) {
        v.push_back(i);
    }
    cout << "v的容量为:" << v.capacity() << endl;
    cout << "v的大小为:" << v.size() << endl;

    //如果单重新指定大小的话,容量并没有变,导致多余的空间浪费
    v.resize(25);
    cout << "用resize(): " << endl;
    cout << "v的容量为:" << v.capacity() << endl;
    cout << "v的大小为:" << v.size() << endl;

    //而我们可以巧用swap()来收缩内存
    vector<int>(v).swap(v);//vector<int>(v)创建了一个匿名对象,会按照v大的大小初始化这个匿名对象容器的大小
                                  //用swap()会将其与匿名函数进行交换,原容器的指针指向匿名对象的容器,原指向匿名对象容器的指针指向原容器
                                  //系统创建完匿名函数后会对匿名对象的指针(地址、内存)进行回收
    cout << "用swap+匿名函数: " << endl;
    cout << "v的容量为:" << v.capacity() << endl;
    cout << "v的大小为:" << v.size() << endl;
}

int main() {
    test07();
    return 0;
}

运行结果:

8、预留空间

(1)功能:预先开辟空间,减少vector在动态扩展容量时的扩展次数。

(2)方式:

cpp 复制代码
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问

示例:

cpp 复制代码
void test08() {
    vector<int> v1;
    int count = 0;
    int* p1 = NULL;
    for (int i = 0; i < 114514; i++){
        v1.push_back(i);

        //每扩容一次就会新开一块内存,则p指向的地址与原来的不同,以此来统计动态开辟的次数。
        if (p1 != &v1[0]) {
            p1 = &v1[0];
            count++;
        }
    } 
    cout << "不预留空间时count: " << count << endl;

    count = 0;
    vector<int> v2;
    v2.reserve(114514);
    int* p2 = NULL;
    for(int i = 0; i < 114514; i++) {
        v2.push_back(i);
        if (p2 != &v2[0]) {
            p2 = &v2[0];
            count++;
        }
    }
    cout << "预留空间时count: " << count << endl;
}

int main() {
    test08();
    return 0;
}

运行结果:

vector小结

  • vector容器是最常用的容器之一,大多时候需要用它来存储数据。
  • 构造函数,元素访问,二维容器,遍历获取,插入删除等操作比较常见。
  • 基础常用的是用[]来获取,访问,构造匿名构造和指定大小构造用得较多,push_back()也用的较多。
  • 注意迭代器的访问,因为它能直接指向内存,且较灵活。
  • 构造、赋值操作中的内存原理需要掌握,扩容时机制是重新开一块新的空间,指针重新指向。
  • 匿名对象的地址、内存会被系统自动回收,因此可以巧用swap和匿名对象来收缩空间,另外注意一下预留空间能减少动态扩容开辟次数。

以上就是关于vector容器常见操作和原理的全部内容了,如果文章有什么错误或者遇到什么问题,欢迎随时和我交流联系。

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
李元豪3 小时前
【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,
数据结构·c++·链表
UestcXiye3 小时前
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
c++·计算机网络·ip·tcp
2401_857610034 小时前
多维视角下的知识管理:Spring Boot应用
java·spring boot·后端
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
代码小鑫4 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
丶Darling.5 小时前
Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)
c++·算法·动态规划·记忆化搜索·回溯
奶味少女酱~5 小时前
常用的c++特性-->day02
开发语言·c++·算法
颜淡慕潇5 小时前
【K8S问题系列 | 9】如何监控集群CPU使用率并设置告警?
后端·云原生·容器·kubernetes·问题解决
我是哈哈hh5 小时前
专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结
c++·算法·动态规划