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语言二维数组看似二维,实则一维)

相关推荐
今天要早睡_8 分钟前
C++ 核心语法速过:命名空间、引用、函数重载与 nullptr 深度解析
android·java·c++
汉克老师42 分钟前
CSP-J 初赛(以满分为目标):第二十七课 《图的遍历——BFS广度优先搜索——像“水波纹”一样,一层一层地搜索》
c++·csp-j·小学生·学c++编程
闻缺陷则喜何志丹1 小时前
【动态规划】P3609 [USACO17JAN] Hoof, Paper, Scissor G
c++·算法·动态规划·洛谷
汉克老师1 小时前
CSP-J 初赛(以满分为目标):第二十六课 《图的遍历——DFS深度优先搜索——从“树的先序遍历”走进真正的图世界》
c++·csp-j·小学生·学c++编程
程序猿编码1 小时前
基于GGML的C++17轻量化语音推理引擎:说话人识别与语音分析技术全解析
开发语言·c++·pytorch·深度学习·神经网络·大模型
影视飓风TIM2 小时前
C++哈希表深度剖析:冲突处理、rehash、迭代器与unordered容器封装
c++·哈希算法·散列表
C++ 老炮儿的技术栈2 小时前
Qt5 使用 QPainter 绘制阿基米德螺线
开发语言·c++·windows·qt·代码化
xxwxx__2 小时前
深入理解 C++ STL:stack、queue 与 deque 从使用到底层实现全解析
开发语言·c++·算法
wuminyu3 小时前
JVM锁膨胀与Futex源码解析
java·linux·c语言·jvm·c++
郝学胜-神的一滴3 小时前
C++11 工程级应用 08:Lambda表达式与Tuple元组
开发语言·jvm·c++·python·程序人生·开源