- vector简介
- vector 的使用
- 初始化
- operator[] 与 at 对容器元素访问
- reserve 、 resize 、assign
- push_back、insert
- emplace_back 原位构造
- vector 是表示可以改变大小的数组的序列容器
- vector 与数组一样,元素使用连续的存储空间,就可以使用常规指针,指向其元素,使用偏移量来访问存储空间中的元素
- vector 与数组不同的是,vector 的大小可以动态变化,容器会自动扩容存储空间
- vector 使用一个动态分配的连续存储空间来存储元素,在插入新元素时存储空间可能需要重新分配,以便增大大小,这意味着分配一个新存储空间要将所有元素移动到其中,就处理时间而言,这是一项相对昂贵的任务,因此,向量不会在每次向容器添加元素时重新分配
- vector 容器可以分配一些额外的存储空间以适应可能的增长,因此容器的实际容量可能大于严格需要的存储容量,即容量大小
- vector 与 array 相比,向量消耗更多的内存,以换取管理存储和以高效方式动态增长的能力
- 与其他动态序列容器(deques、list、和forward_list)相比,vector可以非常高效的访问其元素,并相对高效地从其末尾添加或删除元素,对于在结尾以外的位置插入或删除元素的 *** 作,其性能较差
int main()
{
vector<int> ar1;
vector<int> ar2 = { 12,23,34,45,56,67,78,89,90 };
vector<int> ar3(10, 20);//开辟10个空间,每个空间存放20
vector<int> ar4({ 12,23,34,45,56,67 });
vector<int> ar5(ar1);
vector<int> ar6(std::move(ar2));
}
operator[] 与 at 对容器元素访问
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
return 0;
}
这里首先构造Object对象,然后将Object对象拷贝构造进vector容器,随后析构第一遍构建的Object对象
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl; //有效数据大小
cout << objvec.capacity() << endl; //容量大小
for (int i = 0; i < objvec.size(); i++)
{
cout << objvec[i].Value() << endl;
}
for (int i = 0; i < objvec.capacity(); i++)
{
cout << objvec.at(i).Value() << endl;
}
vector<Object>::iterator it = objvec.begin();
for (; it != objvec.end(); it++)
{
cout << it->Value();
it->Value() += 100;
cout << " " << (*it).Value() << endl;
}
return 0;
}
这里的无论operator[]、at、iterator 都是对其进行引用
当我们不希望对引用的数据进行改变的时候,可以使用常性迭代器
//不希望对其进行改变 常性迭代器
vector<Object>::const_iterator cit = objvec.begin();
for (; cit != objvec.end(); cit++)
{
cout << cit->Value() << endl;
}
并且需要创建常性Value()方法,因为常性迭代器只能指向常性返回数据
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
for (auto x : objvec) //不能将x认为是一个迭代器 推演: x => Object
{
cout << x.Value() << endl;
}
}
auto在这里会不断产生拷贝构造与析构
如果直接进行引用,那么就可以省掉多次的拷贝构造与析构
for (auto &x : objvec) //for(const auto &x : objvec)
{
cout << x.Value() << endl;
}
reserve 、 resize 、assign
- reserve
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
objvec.reserve(10); //预留存储空间
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
}
当空间扩容至10,将原本的对象进行拷贝构造,随后进行析构,容量大小变为10
若new_cap大于capacity(),则所有迭代器,包含尾后迭代器和所有到元素的引用都被非法化
vector<Object>::iterator it = objvec.begin();
Object& obj = objvec.back();
objvec.reserve(10); //预留存储空间
//迭代器与引用非法
这是因为,扩容伴随着拷贝构造与析构,原本我们的迭代器所指的空间或者引用的对象都会被析构掉
- resize
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
objvec.resize(10);
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
}
resize扩容首先,根据上面代码,首先构建了6个Object对象,再将原本有的4个Object对象进行拷贝构造,随后析构原本的对象
resize 与 reserve 不同的是,如果reserve 扩容的大小 < 原本容量的大小,则按照原本的容量进行,而resize 扩容大小 > 原本大小,则不但进行扩容增加空间并且会构造对象,而此时再次进行reszie 扩容大小 < 原本大小,则会对多余对象进行析构
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
objvec.resize(10);
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
objvec.resize(2); //不进行缩容,但是对象个数改变
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
}
- assign
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
for (auto& x : objvec)
{
cout << x.Value() << endl;
}
objvec.assign(10, Object(100));
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
for (auto& x : objvec)
{
cout << x.Value() << endl;
}
return 0;
}
assign的作用就是,将原本容器的全部对象进行析构,随后构建10个对象再将其进行填充
可以看到,首先构建了1个对象,然后析构旧的4个对象,再拷贝构造10个对象,再将一开始构建的对象进行析构
若我们申请空间远远大于实际大小的时候
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
objvec.reserve(10000);
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
//objvec.size() <<<< objvec.capacity();
{
vector<Object> tmp(objvec);
tmp.swap(objvec);
}
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
return 0;
}
可以通过上面的方式将容量缩减回来
我们再块作用域中,通过容器进行初始化tmp,此时tmp的size为4,capacity为4,然后将tmp与objvec进行交换,那么此时objvec的size为4,capacity为4,而tmp的size为4,capacity为10000;当块作用域结束,tmp将被释放掉;从而实现了容量的缩减
reserve 、 resize 、assign这三个函数都会引起迭代器的失效问题
push_back、insert首先从效率上讲,push_base 尾插 比 insert 随机插入,效率要高得多
int main()
{
vector<Object> objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
vector<Object>::iterator it = objvec.begin();
cout << it->Value() << endl;
objvec.push_back(Object(100));
cout << it->Value() << endl;
return 0;
}
我们发现容器进行了扩容,进行了拷贝构造,那么这里的迭代器指向的是原本的容器已经被析构掉,所以迭代器会失效
int main()
{
vector<Object> objvec;
objvec.reserve(10);
objvec = { Object(10),Object(20),Object(30),Object(40) };
cout << objvec.size() << endl;
cout << objvec.capacity() << endl;
objvec.emplace_back(100);
}
objvec.emplace_back(100); 意味着,直接将Object对象构建在尾部空间,与 push_back 不同的地方在于,objvec.push_back(Object(100))
会首先构造一个无名对象,接着进行拷贝构造于容器尾部,emplace_back相当于进行类定位new
object.emplace_back(100); //传输地址 定位new
object.push_back(100); //构建对象 右值引用
object.push_back(Object(10));
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)