先点赞后观看哦!!!谢谢大家!

1.vector的使用说明

1.1vector的介绍

http://www.cplusplus.com/reference/vector/vector/

使用 STL 的三个境界:能用,明理,能扩展 ,那么学习 vector ,我们也是按照这个方法去学
习。

1.2vector的使用

1.2.1vector的定义

1.2.2vector literator的使用

1.2.3vector空间增长问题

1.capacity 的代码在 vs g++ 下分别运行会发现, vs capacity 是按 1.5 倍增长的, g++ 是按 2
倍增长的 。这个问题经常会考察,不要固化的认为, vector 增容都是 2 倍,具体增长多少是
根据具体的需求定义的。 vs PJ 版本 STL g++ SGI 版本 STL
2.reserve 只负责开辟空间,如果确定知道需要用多少空间, reserve 可以缓解 vector 增容的代
价缺陷问题。
3.resize 在开空间的同时还会进行初始化,影响 size
// 如果已经确定 vector 中要存储元素大概个数,可以提前将空间设置足够
// 就可以避免边插入边扩容导致效率低下的问题了
void TestVectorExpandOP ()
{
        vector < int > v ;
        size_t sz = v . capacity ();
        v . reserve ( 100 ); // 提前将容量设置好,可以避免一遍插入一遍扩容
        cout << "making bar grow:\n" ;
        for ( int i = 0 ; i < 100 ; ++ i )
        {
                v . push_back ( i );
                if ( sz != v . capacity ())
                {
                        sz = v . capacity ();
                        cout << "capacity changed: " << sz << '\n' ;
                }
        }
}

1.2.3vector增删改查

1.2.4vector迭代器失效问题(重点)

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对
指针进行了封装 ,比如: vector 的迭代器就是原生态指针 T* 。因此 迭代器失效,实际就是迭代器
底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间 ,造成的后果是程序崩溃 (
如果继续使用已经失效的迭代器,程序可能会崩溃 )
对于 vector 可能会导致其迭代器失效的操作有:
1. 会引起其底层空间改变的操作,都有可能是迭代器失效 ,比如: resize reserve insert
assign push_back 等。
#include <iostream>
using namespace std ;
#include <vector>
int main ()
{
        vector < int > v { 1 , 2 , 3 , 4 , 5 , 6 };
        auto it = v . begin ();
        // 将有效元素个数增加到 100 个,多出的位置使用 8 填充,操作期间底层会扩容
        // v.resize(100, 8);
        // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容
        量改变
        // v.reserve(100);
        // 插入元素期间,可能会引起扩容,而导致原空间被释放
        // v.insert(v.begin(), 0);
        // v.push_back(8);
        // 给 vector 重新赋值,可能会引起底层容量改变
        v . assign ( 100 , 8 );
        /*
        出错原因:以上操作,都有可能会导致vector 扩容,也就是说 vector 底层原理旧空间被释
        放掉,而在打印时,it 还使用的是释放之间的旧空间,在对 it迭代器操作时,实际操作的是
        一块已经被释放的空间,而引起代码运行时崩溃。
        解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector 中的元素,只需给
        it重新赋值即可。
        */
        while ( it != v . end ())
        {
                cout << * it << " " ;
                ++ it ;
        }
        cout << endl ;
        return 0 ;
}
2. 指定位置元素的删除操作 - -erase、
int main ()
{
        int a [] = { 1 , 2 , 3 , 4 };
        vector < int > v ( a , a + sizeof ( a ) / sizeof ( int ));
        // 使用 find 查找 3 所在位置的 iterator
        vector < int > :: iterator pos = find ( v . begin (), v . end (), 3 );
        // 删除 pos 位置的数据,导致 pos 迭代器失效。
        v . erase ( pos );
        cout << * pos << endl ; // 此处会导致非法访问
        return 0 ;
}
erase 删除 pos 位置元素后, pos 位置之后的元素会往前搬移,没有导致底层空间的改变,理
论上讲迭代器不应该会失效,但是:如果 pos 刚好是最后一个元素,删完之后 pos 刚好是 end
的位置,而 end 位置是没有元素的,那么 pos 就失效了。因此删除 vector 中任意位置上元素
时, vs 就认为该位置迭代器失效了。
以下代码的功能是删除 vector 中所有的偶数,请问那个代码是正确的,为什么?
int main ()
{
        vector < int > v { 1 , 2 , 3 , 4 };
        auto it = v . begin ();
        while ( it != v . end ())
        {
                if ( * it % 2 == 0 )
                        v . erase ( it );
                ++ it ;
        }
        return 0 ;
}
int main ()
{
        vector < int > v { 1 , 2 , 3 , 4 };
        auto it = v . begin ();
        while ( it != v . end ())
        {
                if ( * it % 2 == 0 )
                        it = v . erase ( it );
                else
                        ++ it ;
        }
        return 0 ;
}
3. 注意: Linux 下, g++ 编译器对迭代器失效的检测并不是非常严格,处理也没有 vs 下极端。
// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main ()
{
        vector < int > v { 1 , 2 , 3 , 4 , 5 };
        for ( size_t i = 0 ; i < v . size (); ++ i )
                cout << v [ i ] << " " ;
        cout << endl ;
        auto it = v . begin ();
        cout << " 扩容之前, vector 的容量为 : " << v . capacity () << endl ;
        // 通过 reserve 将底层空间设置为 100 ,目的是为了让 vector 的迭代器失效
        v . reserve ( 100 );
        cout << " 扩容之后, vector 的容量为 : " << v . capacity () << endl ;
        // 经过上述 reserve 之后, it 迭代器肯定会失效,在 vs 下程序就直接崩溃了,但是 linux
        下不会
        // 虽然可能运行,但是输出的结果是不对的
        while ( it != v . end ())
        {
                cout << * it << " " ;
                ++ it ;
        }
        cout << endl ;
        return 0 ;
}
程序输出:
1 2 3 4 5
扩容之前, vector 的容量为 : 5
扩容之后, vector 的容量为 : 100
0 2 3 4 5 409 1 2 3 4 5
// 2. erase 删除任意位置代码后, linux 下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了, it 的位置还是有效的
#include <vector>
#include <algorithm>
int main ()
{
        vector < int > v { 1 , 2 , 3 , 4 , 5 };
        vector < int > :: iterator it = find ( v . begin (), v . end (), 3 );
        v . erase ( it );
        cout << * it << endl ;
        while ( it != v . end ())
        {
                cout << * it << " " ;
                ++ it ;
        }
        cout << endl ;
        return 0 ;
}
程序可以正常运行,并打印:
4
4 5
// 3: erase 删除的迭代器如果是最后一个元素,删除之后 it 已经超过 end
// 此时迭代器是无效的, ++it 导致程序崩溃
int main ()
{
        vector < int > v { 1 , 2 , 3 , 4 , 5 };
        // vector<int> v{1,2,3,4,5,6};
        auto it = v . begin ();
        while ( it != v . end ())
        {
                if ( * it % 2 == 0 )
                v . erase ( it );
                ++ it ;
        }
        for ( auto e : v )
                cout << e << " " ;
        cout << endl ;
        return 0 ;
}
========================================================
// 使用第一组数据时,程序可以运行
[ sly@VM - 0 - 3 - centos 20220114 ] $ g ++ testVector . cpp - std = c ++ 11
[ sly@VM - 0 - 3 - centos 20220114 ] $ . / a . out
1 3 5
=========================================================
// 使用第二组数据时,程序最终会崩溃
[ sly@VM - 0 - 3 - centos 20220114 ] $ vim testVector . cpp
[ sly@VM - 0 - 3 - centos 20220114 ] $ g ++ testVector . cpp - std = c ++ 11
[ sly@VM - 0 - 3 - centos 20220114 ] $ . / a . out
Segmentation fault
从上述三个例子中可以看到: SGI STL 中,迭代器失效后,代码并不一定会崩溃,但是运行
结果肯定不对,如果 it 不在 begin end 范围内,肯定会崩溃的。
4. vector 类似, string 在插入 + 扩容操作 +erase 之后,迭代器也会失效
#include <string>
void TestString ()
{
        string s ( "hello" );
        auto it = s . begin ();
        // 放开之后代码会崩溃,因为 resize 20 string 会进行扩容
        // 扩容之后, it 指向之前旧空间已经被释放了,该迭代器就失效了
        // 后序打印时,再访问 it 指向的空间程序就会崩溃
        //s.resize(20, '!');
        while ( it != s . end ())
        {
                cout << * it ;
                ++ it ;
        }
        cout << endl ;
        it = s . begin ();
        while ( it != s . end ())
        {
                it = s . erase ( it );
        // 按照下面方式写,运行时程序会崩溃,因为 erase(it) 之后
        // it位置的迭代器就失效了
        // s.erase(it);
        ++ it ;
        }
}
迭代器失效解决办法:在使用前,对迭代器重新赋值即可

2.vector深度刨析及模拟实现

2.1std::vector的框架接口的模拟实现zzh::vector

https://gitee.com/zhejiang-daniel-wu/my_c_code/blob/master/text_09_25/text_09_25/vector.h

2.2使用memcpy拷贝问题

假设模拟实现的 vector 中的 reserve 接口中,使用 memcpy 进行的拷贝,以下代码会发生什么问
题?
int main ()
{
        zzh::vector < bite::string > v ;
        v . push_back ( "1111" );
        v . push_back ( "2222" );
        v . push_back ( "3333" );
        return 0 ;
}
问题分析:
1. memcpy 是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存
空间中
2. 如果拷贝的是自定义类型的元素, memcpy 既高效又不会出错,但如果拷贝的是自定义类型
元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为 memcpy 的拷贝实际是浅
拷贝。

结论:如果对象中涉及到资源管理时,千万不能使用 memcpy 进行对象之间的拷贝,因为
memcpy 是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

2.2动态二维数组理解

// 以杨慧三角的前 n 行为例:假设 n 5
void test2vector ( size_t n )
{
        // 使用 vector 定义二维数组 vv vv 中的每个元素都是 vector<int>
        bit::vector < bit::vector < int >> vv ( n );
        // 将二维数组每一行中的 vecotr<int> 中的元素全部设置为 1
        for ( size_t i = 0 ; i < n ; ++ i )
                vv [ i ]. resize ( i + 1 , 1 );
        // 给杨慧三角出第一列和对角线的所有元素赋值
        for ( int i = 2 ; i < n ; ++ i )
        {
                for ( int j = 1 ; j < i ; ++ j )
                {
                        vv [ i ][ j ] = vv [ i - 1 ][ j ] + vv [ i - 1 ][ j - 1 ];
                }
        }
}
zzh::vector<bit::vector<int>> vv(n) ; 构造一个 vv 动态二维数组, vv 中总共有 n 个元素,每个元素
都是 vector 类型的,每行没有包含任何元素,如果 n 5 时如下所示:
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐