vector

vector

1、string的补充

1.1、拷贝构造的现代写法

现在很多人偏向于把string的拷贝构造设计成这样:

拷贝构造这样设计,进行了两步:

  1. 利用参数s存的字符串,构造出string串tmp
  2. 将tmp与*this交换

拷贝构造的现代写法,核心就是代码复用。

由于一开始*this未初始化,可能有程序崩溃的风险。所以我们可以给缺省值:

1.2、重载=的现代写法

思路与拷贝构造的现代写法是一样的,都是代码复用。

甚至我们可以让重载=的代码更简洁一点:

我们设计的参数是string s,这是传值传参,string自定义对象传值传参时自动调用拷贝构造。然后再用这份拷贝与*this交换。

这里巧妙的一点是,我们利用传值传参自动调用拷贝构造的特点,让编译器自动为我们进行拷贝构造,所以我们不需要显示写拷贝构造。

但有一点需要强调:以上现代写法,均没有提升效率。该申请资源还是得申请资源,该释放资源还是得释放资源。

1.3、SBO

有一个问题:

cpp 复制代码
void test1()
{
	std::string s1("Hello world!");
	std::string s2;
}

s1、s2,哪一个占用空间大?

按照我们之前自主实现string的思路,声明私有成员变量时,有三个:

  • char* _str(存储的指针指向资源)
  • size_t _size
  • size_t _capacity

相同的机器下,三个私有变量大小相同(64位:char*, size_t都是8;32位:char*, size_t都是4)。所以我们推理,大小都应是12/24。


结果却是28/40?

调试看看vs2022下string对象里面到底有啥?

有时候vs2022调试时看不到string对象里面具体包含的成员。


我们可以在vs2022做如下修改:

工具 ---> 选项 ---> 调试 ---> 常规 ---> 勾选上在变量窗口中显示对象的原始结构

其中,_Mysize就是_size,_Myres就是_capacity。

还有_Buf,_Ptr。

实际上,这里使用了SBO。

SBO = Small Buffer Object,小缓冲区对象

也叫SSO = Small String Optimization,小字符串优化

简单来说,就是内容小于等于15字节,存放入_Buff字符数组中,就不需要申请资源。

而内容大于15字节,就必须申请资源,此时_Ptr指向申请的空间。

1.4、引用计数的写时拷贝

以上是MSVC管理资源的策略。

而早年g++版本的策略,是这样的:

在没有任何一个对象资源上的内容需要被修改时,其实就是浅拷贝。

只不过,多了一个引用计数:

于是,每次要销毁一个对象,如果引用计数大于1,只需要引用计数 - 1;如果引用计数等于1,才释放空间。

这看起来非常高效,但是这种策略只解决了一个问题:指向同一块空间会导致多次析构。

还有一个问题没有解决:改变一个对象,其它对象跟着改变。

这时就必须拷贝了:

  • 开辟新空间,拷贝内容。新对象的引用计数起始为1
  • 原对象的引用计数 - 1

这就是引用计数的写时拷贝。

采取写时拷贝策略的原因是:有的需要拷贝对象行为的场景,并不会修改对象。比如传值返回包含大资源的对象。

但是新版Linux的g++舍弃了这一策略:

原因有三:

  1. 多线程情况下,维护引用计数的线程安全有一定代价。
  2. 动态库下,会存在一些问题。
  3. C++11以后,编译器有各种优化,传值返回的代价较低,而且有了移动构造、移动赋值的方式。

2、vector的使用

构造

想打印?三种方法:

  • 重载
  • 范围for
  • 迭代器

2.1、initializer_list

我们还可以这样初始化vector< int >:

其中,我们用花括号包含了一段序列,这段序列的类型是:initializer_list。

initializer_list类包含三个成员:

我们看到成员包含迭代器。所以initializer_list类也支持范围for:

2.2、vector的扩容机制

像string一样,我们设计代码观察vector的扩容机制。

MSVC下:

1.5倍扩容。

g++下:

2倍扩容。

为了减少扩容次数,我们可以用:reserve()。

2.3、其它接口

resize()

resize()的作用是:缩容就删除(部分)数据,扩容就用指定内容填充。

那么我们就可以用特定值初始化,也可以理解为批量操作:


insert() && erase()

vector的插入和删除接口是通过迭代器实现的:



2.4、emplace简单介绍

我们定义一个多参数的类行:

然后创建一个vector顺序表,每一个元素是AA类型的:

我们不难知道,里层花括号的逻辑代表:多参数隐式类型转换,外层花括号的逻辑代表:initialize_list类型对象。

但是不能直接打印:

所以我们可以这样打印:

emplace的行为与insert相似,emplace_back与push_back相似:

但是emplace_back可以这样用:

也就是传入emplace_back的参数,可以是:

  • AA对象
  • 构造AA对象的参数

emplace也是类似。

但是push_back和insert只能传对象。

2.5、几道算法题

只出现一次的数字

这道题的核心思路是:相同的值,按位异或后得0。

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (int i = 0; i < nums.size(); ++i)
        {
            ret ^= nums[i];
        }
        return ret;
    }
};

杨辉三角

题目中要求我们返回数据的类型是:vector<vector<int>>。

这个类型的意思是,有一个vector顺序表v1,v1的每一个元素,都是vector<int>顺序表。

我们可以理解为,最顶上的模板类实例化出了两种类:T = vector< int >, T = int。然后这两种类的关系如上图。

cpp 复制代码
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        //开辟numRows个空间,对应numRows行序列
        vector<vector<int>> v1(numRows);
        for (int i = 0; i < numRows; ++i)
        {//每行序列开辟空间,初始化成1
            v1[i].resize(i+1, 1);
        }
        //行
        //从第3行开始
        for (int i = 2; i < numRows; ++i)
        {
            //列
            //从第2个数开始,一直到i-1
            for (int j = 1; j < i; ++j)
            {
                v1[i][j] = v1[i-1][j-1] + v1[i-1][j];
            }
        }
        return v1;
    }
};

上面代码演示中,v1ij左右两个\[\]代表不同的重载:

cpp 复制代码
vector<int>& operator[](size_t n)// 左边的[]
{
	// ...
}

int& operator[](size_t n)// 右边的[]
{
	// ...
}

而重载放入类域,则可能会inline内联展开。

我们可以将vector的二维数组,与C语言的二维数组做一个比较。(C语言二维数组看似二维,实则一维)

相关推荐
WL学习笔记9 小时前
C & C++ 内存管理
c++·c++内存管理
宛 禾12 小时前
Qt 控件入门:QLabel 从拖拽到对象树,一篇讲透
c++·qt·学习
hetao173383712 小时前
联盟场-提高组-R7 & ZYZSC-S-Round 11
c++·算法
程序喵大人12 小时前
【C++入门】编译链接模型 - 07 链接器怎样把符号对上号
开发语言·c++·编译器·编译链接·链接器
局部有解12 小时前
Hot100 - 三数之和
数据结构·c++·算法
longlongzihan13 小时前
C++ 常用数据结构速查:vector、stack、queue、list、树、图、哈希表等声明与核心操作
数据结构·c++·list
别动我齐刘海13 小时前
实时控制学习2
linux·c++·人工智能·学习·算法
hetao173383714 小时前
校内场-提高组 ZYZSC-S-Round 2
c++·算法
hetao173383715 小时前
2026-10-14 hetao1733837 的刷题记录
c++·算法
浅念-15 小时前
动态规划:线性DP——多状态DP与子数组专题笔记
c++·笔记·算法·leetcode·职场和发展·动态规划·力扣