初学vector

news2024/11/27 21:04:47

目录

string的收尾

拷贝构造的现代写法:

浅拷贝:

 拷贝构造的现代写法:

swap函数:

内置类型有拷贝构造和赋值重载吗?

完善拷贝构造的现代写法:

赋值重载的现代写法:

 更精简的现代写法:

初学vector

插入数据:

访问vector的数据。

 理解vector:

reserve:

resize

vector如何进行扩容:

算法题目

题目1:

 题目2:


string的收尾

拷贝构造的现代写法:

浅拷贝:

 其中,s1的_str指向字符串hello world,用s1拷贝构造s2:

s2(s1);

当我们发生浅拷贝时,我们的s1的_str和s2的_str指向同一块空间:

如图所示:

这就是所谓的浅拷贝。

浅拷贝的定义:

拷贝对象和源对象指向的空间是相同的。

浅拷贝造成的问题:

1:当s1调用完毕析构函数后,s2指向的空间也被析构了,s2也调用自己的析构函数,所以会导致一块空间被析构两次的问题。

2:对s1或s2其中一个对象的修改会导致另一个对象也发生变化。

 拷贝构造的现代写法:

string(const string&s)
		{
			string tmp(s._str);
			swap(_str, tmp._str);
			swap(_size, tmp._size);
			swap(_capacity, tmp._capacity);
		}

问题1:为什么这里的参数需要加上const

答:我们函数的目的是为了用s进行拷贝构造一个新的对象,所以我们最好不要对s本身进行修改,所以我们加上const表示s是只读的。

我们对代码进行理解:

string tmp(s._str);

我们可以通过构造函数实现

我们首先调用构造函数

构造完毕如图所示:

swap(_str, tmp._str);
swap(_size, tmp._size);
swap(_capacity, tmp._capacity);

 接下来调用swap函数:

表示交换两个目标的值,我们把tmp的全部都与this指针指向的都西昂进行交换,如图所示:

 那么我们的s2指向的内容就是"hello world"了,并且我们的s2和s1的_str并不相同,我们实现了深拷贝。

我们进行实验:

void test_string1()
	{
		string s1("hello world");
		string s2(s1);
		cout << s2.c_str() << endl;
	}

 

 我们完成了拷贝构造的深拷贝。

问题1:如图:

我们tmp现在指向的是原本属于s2的那部分空间,但是我们的s2并没有进行初始化,所以我们的s2指向的是随机值,我们的tmp是一个临时对象,函数栈帧调用完毕就会调用析构函数,我们delete函数对于野指针会报错。

 我们如何预防这种问题呢?

答:我们可以使用初始化列表先把s2置为空指针,如代码所示:

string(const string&s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(_str, tmp._str);
			swap(_size, tmp._size);
			swap(_capacity, tmp._capacity);
		}

 delete对于空指针不做处理,所以不会报错。

swap函数:

s1.swap(s2);
swap(s1, s2);

这两句代码有什么区别吗?

我们首先要清楚,这里调用了不同的swap函数,第一个调用的是string的接口swap函数:

第二个调用的是标准库里面的swap函数:

 标准库的swap函数是用类摸板,通过调用三次拷贝构造来实现的。

那种更好更高效呢?

答:对于string的调用无疑是第一种更好更高效,因为我们的第二种调用了三次拷贝构造,调用三次string的拷贝构造,效率太低。

但是第一种是string的接口,我们的目的是模拟实现string,所以我们也要对string的swap函数进行模拟实现。

void swap(string&s)
		{
			swap(_str, s._str);
			swap(_size, s._size);
			swap(_capacity, s._capacity);
		}

这样写对吗?

不对,原因如下:

因为swap是在函数内部,首先在局部域找swap函数,找到了我们自己定义的swap函数,但是我们自己定义的swap函数只有一个参数,而我们调用swap函数却有两个参数,所以会报错,我们可以这样修改。

void swap(string&s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

我们使用域作用限定符,表示我们调用的swap是标准库里面的swap函数。

 但是我们的标准库里面的swap函数需要调用三次拷贝构造,这个该怎么处理呢?

我们不需要考虑这个问题,因为我们swap调用的参数在这里全部是内置类型,对内置类型的拷贝构造不会造成效率的损失。

内置类型有拷贝构造和赋值重载吗?

答:原则上并没有,但是为了匹配摸板,不得不有,但是我们调用内置类型的拷贝构造和赋值重载和内置类型的初始化和赋值没有区别。

完善拷贝构造的现代写法:

string(const string&s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

我们可以把原来的三个swap函数写成一个。

这个swap的本质是调用我们自己实现的string类里面的swap函数,其实等价于

this->swap(tmp);

赋值重载的现代写法:

string s1("hello world");
string s3("hello bit");
s1 = s3;
string&operator=(const string&s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}
			return *this;
		}

我们调用赋值重载函数需要返回*this,所以我们要用string&来接收。

我们进行判断,如果this和s的地址不相同,表示我们的s1和s3指向的不是同一块空间,我们调用赋值函数,首先调用拷贝构造,如图所示:

 我们直接把tmp和s1上的内容进行交换即可,原因是我们实现的拷贝构造是深拷贝,所以tmp和s3的内容相同,但是指向的空间是不同的,然后我们再把s1和tmp进行交换,交换之后的结果如图所示:

 更精简的现代写法:

string&operator=(string s)
		{
			swap(s);
			return *this;
		}

 我们的参数不传引用,既然不传引用,那么s就是s3的拷贝,s和s3指向的空间并不相同,然后我们调用swap函数即可。

初学vector

 vector是一个可以更改元素的数组,vector中可以存一种任意类型的数据。

插入数据:

#include<iostream>
#include<vector>
using namespace std;
void test_vector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	
	cout << endl;
}
int main()
{
	test_vector1();
	return 0;
}

 我们进行调试:

 这就是所谓的插入函数。

访问vector的数据。

方法1:通过[]来进行访问:

vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
cout << endl;

这种方法并不是万能的,string和vector中都有[],但是list中没有[] 

方法2:通过迭代器访问:

vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

这种方法是万能的,因为每一个接口都有迭代器。

方法3::通过范围for访问

for (auto ch : v)
	{
		cout << ch << " ";
	}
	cout << endl;

范围for的底层其实是迭代器的另一种表达,所以没有迭代器也就没有范围for

 

 理解vector:

 一个是内容是字符类型的vector,一个是string类。

他们是不同的,因为str的本质是字符串,而vstr的本质是数组,字符串的末尾一定是'\0',而数组没有这个要求。

string可以比较,以ascll码的形式进行比较,但是vector最好不要比较,原因是vector中不仅可能有内置类型,也有可能有自定义类型的内容。

reserve:

 reserve函数的作用是修改容量。

我们思考一个问题:reserve函数可以缩容吗?

答:并不能,我们进行证明:

vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.reserve(10);
	cout << v.capacity() << endl;
	v.reserve(4);
	cout << v.capacity() << endl;

 reserve函数并不能够缩容,原因是:因为缩容的代价太大,时间效率是最重要的,我们采用以空间换时间的思想,不对空间进行缩容。

resize

 这里的size_type和value_type是什么意思?

size_type其实就是无符号整型的意思。

value_type就是vector中存放的数据类型。

 这是什么意思?

这里的value_type我们可以等价为T().当我们这样写代码时:

v.resize(8);

 我们没有填写第二个参数,第二个参数的缺省值是value_type()。

这个value_type()本质是T(),假如我们的vector中的数据类型是string类型,那这个T()就是string类型的匿名对象,假如我们的vector中的数据类型是int类型,那这个T()就是int类的匿名对象也就是0,加入我们的vector中的数据类型是一个指针,那么这个T()就是指针的匿名对象也就是空指针。

我们可以这样吗?

不行,对于整形 浮点型,我们可以把其看作0,对于指针,我们可以把其看作空指针,但是对于自定义类型呢?自定义类型并不能用0初始化。

 所以不能这样写。

vector如何进行扩容:

void TestVqectorExpand()
{
	size_t sz;
	vector<int> v;
	sz = v.capacity();
	cout << "making v 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';
		}
	}
}

这串代码可以检测我们的vector是如何进行扩容的。

 我们可以发现,扩容大概是每次扩容1.5倍,这样的好处是什么?

答:倍数过大时容易造成空间浪费,倍数过小容易导致频繁扩容。

扩容是有消耗的,为了防止扩容,我们可以使用reserve提前开好空间。

 

算法题目

题目1:

136. 只出现一次的数字 - 力扣(Leetcode)

 我们需要了解^,异或表示相同为0,不同的话为1(针对的是二进位制)

 这个数组中只出现一次的元素是4,我们如何找到4呢,我们可以进行异或:

我们知道相同的数字异或的结果为0,并且异或满足结合律

 我们可以把相同的数字放在一起:

 1异或1的结果为0,2异或2的结果为0,0异或0的结果为0,4异或0的结果为4.

异或:a^b,我们要进行判,加入a和b都为0时,结果为0,加入a或b其中一个为0,那结果就是另一个值,当a和b都不同且不为0,我们写出他们两个对应的二进位制,相同的二进位制的结果为0,不同的二进位制的结果为1,求出的二进位制对应的数字就是我们要的结果。

并且异或满足结合律,一组数组中,加入有相同的数字,这些数字我们可以把他们放在一起进行计算,计算的结果也是0。

所以我们可以这样写代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret=0;
        for(auto ch:nums)
        {
            ret^=ch;
        }
        return ret;
    }
};

 题目2:

118. 杨辉三角 - 力扣(Leetcode)

 

 假如我们用c语言写的话:

 我们需要先malloc一个指针数组

 我们需要再在指针数组上进行malloc申请空间。

 我们在释放的时候,也需要先把一维数组释放掉,再释放二维数组。

可以发现,c语言写这道题目操作难度太大,我们可以用c++来写。

 这里表示二维数组,generate首先是一个vector,vector中的元素的类型是vector<int>

 

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        vv.resize(numRows);
        for(size_t i=0;i<vv.size(),i++)
        {
            vv[i].resize(i+1);
            vv[i][0]=vv[i][vv[i].size()-1]=1;
        }
        for(size_t i=0;i<vv.size();++i)
        {
            for(size_t j=0;j<vv[i].size();++j)
            {
                vv[i][j]=vv[i-1][j]+vv[i-1][j-1];
            }
        }
        return vv;
    }
};

 我们对代码进行逐步分析:

 我们首先创建一个二维数组vector,接下来调用resize函数进行初始化,我们的vv的元素个数为numRows

 我们调用resize函数,不传第二个参数,那第二个参数就是vector<int>类型的匿名对象,也就是0.

我们看杨辉三角有什么特点:

我们可以发现杨辉三角的每一行的首元素和尾元素都为1,并且每一行的元素个数都比前一行的元素个数多1个。

 我们遍历数组的每一个元素,首先在每一个元素位置开辟对应的元素个数,然后把每一行的首元素和尾元素都置为空。

 接下来,我们只需要实现这些中间元素即可。

 我们发现中间的每一个元素都为这个元素正上方的元素和正上方的左面一个单位的元素之和。

接下来,我们返回vv即可

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/345291.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1.5配置NBMA和P2MP网络类型

1.3.3实验5:配置NBMA和P2MP网络类型 1. 实验需求 控制OSPF DR的选举修改OSPF的网络类型2. 实验拓扑 配置NBMA和P2MP网络类型实验拓扑如图1-13所示。 图1-13 配置NBMA和P2MP网络类型 3. 实验步骤 帧中继的配置如图1-14和图1-15所示

软件测试——测试用例之场景法

一、场景法的应用场合 场景法主要用于测试软件的业务流程和业务逻辑。场景法是基于软件业务的测试方法。在场景法中测试人员把自己当成最终用户&#xff0c;尽可能真实的模拟用户在使用此软件的操作情景&#xff1a; 重点模拟两类操作&#xff1a; 1&#xff09;模拟用户正确…

仓库拣货标签应用案例

使用场景&#xff1a;富士康成都仓库 解决问题&#xff1a;仓库亮灯拣选&#xff0c; 提高作业效率和物料明晰展示仓库亮灯拣选使用场景&#xff1a;京东仓库 解决问题&#xff1a;播种墙分拣&#xff0c;合单拣货完成后按订单播种播种墙分拣使用场景&#xff1a;和尔泰智能料…

Ubuntu 20中安装SNAP

Ubuntu 20中安装SNAP0 前言1 下载SNAP安装包2 安装SNAP详细步骤0 前言 姊妹篇《Ubuntu 20中安装snaphu》 SNAP是欧空局领导开发的开源的遥感数据处理软件&#xff0c;主要支持欧空局的数据&#xff0c;如sentinel-系列等。SNAP下载官网&#xff1a;https://step.esa.int/main…

算法训练营 day46 动态规划 最后一块石头的重量 II 目标和 一和零

算法训练营 day46 动态规划 最后一块石头的重量 II 目标和 一和零 最后一块石头的重量 II 1049. 最后一块石头的重量 II - 力扣&#xff08;LeetCode&#xff09; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xf…

百度百科创建词条教程合集分享,赶紧收藏起来

每一个企业、品牌、人物、产品想要提升自己的知名度&#xff0c;都要创建一个属于自己的百度百科词条&#xff0c;互联网时代&#xff0c;百度搜索引擎的地位是不可撼动的&#xff0c;每天都有上亿的用户在百度上搜索相关内容&#xff0c;百度百科词条在网络营销中占据着举足轻…

Oracle对象——视图之简单视图与视图约束

文章目录什么是视图为什么会使用视图视图语法案例简单视图的创建更改数据基表&#xff0c;视图数据会变化么&#xff1f;更改视图数据&#xff0c;基表数据会变更么&#xff1f;带检查约束的视图结论创建只读视图&#xff08;MySQL不支持&#xff09;总结什么是视图 视图是一种…

【项目精选】高校固定资产管理系统(论文+视频+源码)

点击下载源码 随着计算机信息技术的发展以及对资产、设备的管理科学化、合理化的高要求&#xff0c;利用计算机实现设备及资产的信息化管理已经显得非常重要。 固定资产管理系统是一个单位不可缺少的部分。但一直以来人们使用传统的人工方式管理固定资产的信息&#xff0c;这种…

C++STL剖析(六)—— set和multiset的概念和使用

文章目录&#x1f31f; 前言&#x1f351; 树型结构和哈希结构&#x1f351; 键值对1. set的介绍和使用&#x1f351; set的模板参数列表&#x1f351; set的构造&#x1f351; set的使用&#x1f345; insert&#x1f345; find&#x1f345; erase&#x1f345; swap&#x1…

Java基础之异常

目录1 异常1.1 异常的概述1.2 常见异常类型1.3 JVM的默认处理方案1.4 编译时异常的处理方式1.4.1 异常处理之 try ... catch ... [ktʃ]&#xff08;捕获异常&#xff09;1.4.2 异常处理之 throws&#xff08;抛出异常&#xff09;1.5 Throwable 的成员方法1.6 编译时异常和运行…

H5 抽奖页面

好久之前就想去写一个这样的示例了&#xff0c;然后就忘了…&#x1f635; &#x1f600;效果 &#x1f587;在线链接 https://linyisonger.github.io/H5.Examples/?name./34.%E6%8A%BD%E5%A5%96%E9%A1%B5%E9%9D%A2.html &#x1f6a7; 图片可能会丢失&#xff0c;都是在网…

大尺度衰落与小尺度衰落

一. 大尺度衰落 无线电磁波信号在收发天线长距离&#xff08;远大于传输波长&#xff09;或长时间范围发生的功率变化&#xff0c;称为大尺度衰落&#xff0c;一般可以用路径损耗模型来描述&#xff0c;路径损耗是由发射功率在空间中的辐射扩散造成的&#xff0c;根据功率传输…

Hadoop开启Yarn的日志监控功能

1.开启JobManager日志 &#xff08;1&#xff09;编辑NameNode配置文件${hadoop_home}/etc/hadoop/yarn-site.xml和mapred-site.xml 编辑yarn-site.xml <!-- Site specific YARN configuration properties --> <configuration><property><name>yarn.…

如何排查网页在哪里发生了内存泄漏?

今天我们来学习用 devtool 的 Performance 和 Memory 工具来找出网页哪里发生了内存泄漏。 Performace 面板 首先我们打开浏览器的 devtool&#xff0c;选择 Performance&#xff08;性能&#xff09;面板&#xff0c;然后将 Memory 选项勾选上。不勾选的话&#xff0c;就不会…

火爆全网的ChatGPT,可以自己上手搭建了。

没有人不知道ChatGPT了吧&#xff1f; ChatGPT&#xff0c;发布于2022年11月30日&#xff0c;来自人工智能研究实验室OpenAI&#xff0c;是一款全新聊天机器人模型&#xff0c;一款人工智能技术驱动的自然语言处理工具。 5天用户破百万&#xff0c;2个月活跃用户破亿。ChatGP…

大学生常用python变量和简单的数据类型、可迭代对象、for循环的3用法

文章目录变量和简单的数据类型下划线开头的对象删除内存中的对象列表与元组debug三酷猫钓鱼记录实际POS机小条打印使用循环找乌龟可迭代对象&#x1f4d7;理解一&#x1f4d8;理解二2️⃣什么是迭代器✔️注意3️⃣迭代器对象4️⃣有关迭代的函数for循环的3用法&#x1f338;I …

C++ 算法主题系列之贪心算法的贪心之术

1. 前言 贪心算法是一种常见算法。是以人性之念的算法&#xff0c;面对众多选择时&#xff0c;总是趋利而行。 因贪心算法以眼前利益为先&#xff0c;故总能保证当前的选择是最好的&#xff0c;但无法时时保证最终的选择是最好的。当然&#xff0c;在局部利益最大化的同时&am…

Seata-Server分布式事务原理加源码 (五) - Seata配置Nacos注册中心和配置中心

Seata配置Nacos注册中心和配置中心 Seata支持注册服务到Nacos&#xff0c;以及支持Seata所有配置放到Nacos配置中心&#xff0c;在Nacos中统一维护&#xff1b; 高可用模式下就需要配合Nacos来完成 具体配置如下 注册中心 Seata-server端配置注册中心&#xff0c;在registr…

【Android学习】下载jar慢和gradle慢的情况

目录 问题出现的原因 解决方法 解决Gradle下载问题&#xff1a;手动安装 解决jar包下载慢问题&#xff1a;更改下载源 问题出现的原因 国内访问谷歌被墙导致访问速度慢或者干脆无法下载 解决方法 解决Gradle下载问题&#xff1a;手动安装 访问官网Gradle | Release Candi…

配置可视化-基于form-render的无代码配置服务(一)

背景 有些业务场景需要产品或运营去配置JSON数据提供给开发去使用&#xff08;后面有实际业务场景的说明&#xff09;&#xff0c;原有的业务流程&#xff0c;非开发人员&#xff08;后面直接以产品指代&#xff09;把数据交给开发&#xff0c;再由开发去更新JSON数据。对于产…