详解Vector(1)

Vector(1)

在这里插入图片描述

Vector是表示大小可以改变的数组的序列容器。(其实就是一个顺序表)

Vector是一个标准的模版,第一个模版参数是它要存什么数据类型,第二个参数可以先不关心。

构造函数:

析构函数:

它会自动调用,我们无需去管。

赋值:

遍历

它的遍历同样有3种方式:

第一种是下标+方括号;第二种是迭代器;

for (size_t i = 0; i < v3.size(); i++)
{
	cout << v3[i] << " ";
}
cout << endl;

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

for (auto e : v3)
{
	cout << e << " ";
}
cout << endl;

vector的默认扩容机制

//vector的默认扩容机制
void TestVectorExpand()
{
	vector<int> v;
	size_t sz;
	sz = v.capacity();
	cout << "capacity changed: " << sz << '\n';
	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';
		}
	}
}

同样的,我们还是可以采用提前reserve的方式,减少扩容次数。

同样的,我们可以看到,我们的n给了多少,并不代表开的空间就是多少,可能会多开。

同时可以看出,当给的n比capacity要小,vector选择了不缩容,这一点和string没有保持一致,为什么?当然,是历史原因。

缩容

void test_vector2()
{
	vector<int> v(10, 1);
	v.reserve(20);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.reserve(15);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.reserve(5);
	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

int main()
{
	test_vector2();

	return 0;
}

可以看到没有缩容,capacity没有任何变化。

在Linux也没有缩容,符合了文档。而string是会缩容的。

缩容占不到什么便宜,我们要注意:缩容并不是我们想象中从一块空间的中间某个部分想释放就释放,而是要从哪申请就从哪释放。

resize

我们看一下这三种不同的情况:

后两者归为一类,前者为一类。第一种情况一般不会缩容,但是会删除数据。给5就是只保留前5个数据;大于size就是要插入数据,空间不够就扩容;

member type就是在类里面typedef的甚至是内部类的类型。所以我们可以看到value_type就是第一个模版参数,也就是T。

所以对于resize来说,如果给定了值就用这个值补到n,没有给定就用T类型的缺省值。就是默认构造构造的值。

如果n大于capacity,就会扩容。

void test_vector3()
{

	vector<int> v(10, 1);
	v.reserve(20);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(15,2);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(25, 3);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(5);
	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

int main()
{
	test_vector3();

	return 0;
}
operator[]和at

我们之前就说过,operator[]会越界断言,而at是越界会抛异常。

front和back

就是取首和尾的数据。

data

是底层的那个指针,但我们一般不会返回data。

assign

assign是一种赋值,但是一般我们都用operator[]赋值。这个不常用。

assign可以用n个value赋值,或者用迭代区间赋值。

push_back和pop_back

可以看到支持尾插和尾删,和string一样,没有直接支持头插和头删。

想头插或头删就得用insert或者erase。

insert和erase

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个insert是比之前的string的insert简洁了很多的。但这里不支持下标了,只支持迭代器。(是为了兼容,毕竟在链表这样的数据结构中,是没有下标的)

void test_vector4()
{
	vector<int> v(10, 1);
	v.push_back(2);
	v.insert(v.begin(), 0);

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

	v.insert(v.begin() + 3, 10);//在第3个位置之前插入10

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

int main()
{
	test_vector4();

	return 0;
}

当然,insert和erase都要挪动数据,效率不高。

swap

为什么要实现swap,和string的理由一样,怕交换的时候效率低。

clear

和之前一样,清掉所有的数据。


可以看出,vector相比string简洁了很多。且和string有很多相似性,本质都是顺序表或者说数组。

emplace系列我们暂时不去看。

然后,也有一个全局的swap,和string一样。

vector支持比较大小。

实现的是小于和等于,其他的进行了复用。

一般我们用不上vector的比较大小。

vector不支持流插入和流提取。

为什么?vector的输入输出是具有很多不确定性的。

自己要实现其实很方便。

int x;
cin >> x;
v.push_back(x);
vector<int>v1(5, 0);
for (size_t i = 0; i < 5; i++)
{
	cin >> v1[i];//方括号返回的是引用
}

for (auto e:v1)
{
	cout << e << ",";
}
cout << endl;
思考:vector能够替代string吗?

底层都是char*的指针。

但是是不行的。

其中底层的一个重大区别在于’\0’。string的’\0’很好兼容了C语言。除此之外还有很多的问题,总之专门将string划分出来是很有意义的。字符数组用得很多,且有很多专用的需求。比如还有编码的需求。

vector存放string类型
void test_vector5()
{
	vector<string> v1;
	string s1("xxxx");
	v1.push_back(s1);

	v1.push_back("yyyyy");//const char*可以隐式类型转换成string
    
}

这样隐式类型转换后用起来更方便简洁。

然后我们需要注意的是:

for (auto e : v1)
{
	cout << e << " ";
}
cout << endl;

在vector中存放的类型为string时,需要谨慎,因为范围for是逐次去拷贝v1中的值给e,这样拷贝的代价是比较大的。而且拷贝构造还要深拷贝,要开空间然后再拷贝数据。

所以在使用范围for时我们加引用,如果不改变原来的值,就把const也加上。

for (const auto& e : v1)
{
	cout << e << " ";
}
cout << endl;
vector里面可以存vector
//二维数组 10*5
vector<int> v(5, 1);
vector<vector<int>> vv(10,v);

本文到此结束,敬请期待后文。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值