网站首页 > 基础教程 正文
std::vector的使用频率比较高, 有必要盘盘它。
0.概念
std::vector 是 C++ 标准库中的一个动态数组模板类。它允许您存储和操作一系列连续的元素。以下是关于 std::vector 的几个关键点:
- 动态大小:与常规数组不同,std::vector 的大小可以在运行时改变。您可以使用 push_back(), pop_back(), insert(), erase(), 和其他成员函数来更改向量的大小。
- 连续存储:std::vector 保证其元素在内存中是连续存储的,这使得访问和修改元素非常快速。
- 容量增长:当您向 std::vector 添加元素时,它会自动重新分配内存以容纳更多的元素,通常会按比例增长其容量。
- 迭代器:std::vector 提供了随机访问迭代器,这使得它可以像常规数组一样进行索引,但同时也支持许多 STL(标准模板库)算法。
- 内存管理:std::vector 负责自己的内存管理,它会自动调整其大小以适应元素的需要,并在必要时重新分配内存。
- 类型安全:由于 std::vector 是模板类,因此它可以存储任何类型的元素,并且类型安全。
- 效率:虽然 std::vector 需要维护内部指针和数据结构,但大多数操作(如添加或删除元素)的开销都相对较低。对于许多应用,使用 std::vector 比使用常规数组或手动管理内存更高效。
1.示例
代替字符数组
#include<iostream>
#include <vector>
#include <memory>
void main() {
//模拟代替字符数组 char buf[4096];
std::vector<char> buf(4096);
strcpy(buf.data(), "hello world");
std::cout << strlen(buf.data()) << std::endl;
std::cout << buf.data() << std::endl;
//当然现在智能指针使用替换
//std::unique_ptr<char[]> ptr(new char[4096]);
}
常规操作
#include<iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<std::string> vecS({"one", "two"});
//添加
vecS.push_back("three");
vecS.emplace_back("four");
vecS.insert(vecS.begin(), "zero");
vecS.insert(vecS.end(), { "five", "six" });
vecS.emplace(vecS.end(), "seven");
vecS.clear();//全部删除
vecS.assign(7, "ok");//分配7个串, 每个都是"ok"
vecS.assign({"zero", "one", "two", "three", "four", "five", "size", "seven"});
//访问
if (0 == vecS[2].compare("two")) {
std::cout << vecS[2] << std::endl;
//打印输出: two
}
//遍历
for (auto elem : vecS) {
//for (std::string elem : vecS) {
std::cout << elem << " ";
}
std::cout << std::endl;
//打印输出: zero one two three four five six seven
for (auto& elem : vecS) {
//for (std::string& elem : vecS) {
//for (const auto& elem : vecS) {
//for (const std::string& elem : vecS) {
std::cout << elem << " ";
}
std::cout << std::endl;
//打印输出: zero one two three four five six seven
std::for_each(vecS.begin(), vecS.end(),
[](std::string& elem) { std::cout << elem << " "; });
std::cout << std::endl;
//打印输出: zero one two three four five six seven
//查找
std::vector<std::string>::iterator it;
it = std::find(vecS.begin(), vecS.end(), "three");
it = std::find_if(vecS.begin(), vecS.end(),
[](std::string& elem) { return 0 == elem.compare("three"); });
//排序 (先打乱, 然后再0-7排序)
std::sort(vecS.begin(), vecS.end());//默认的,打乱了0-7排序
std::sort(vecS.begin(), vecS.end(),
[](const std::string& x, const std::string& y) {
//只是演示,不考虑效率
int x1 = 0, y1 = 0;
std::string cmp[] = { "zero", "one", "two", "three", "four", "five", "size", "seven" };
for (int i = 0; i < sizeof(cmp) / sizeof(cmp[0]); i++) {
if (x1 == 0 && 0 == cmp[i].compare(x)) {
x1 = i;
}
if (y1 == 0 && 0 == cmp[i].compare(y)) {
y1 = i;
}
}
return x1 < y1;
});
//删除
vecS.pop_back();//删除最后一个"five"
vecS.erase(vecS.begin());//删除首个"zero"
vecS.erase(std::find(vecS.begin(), vecS.end(), "three"));
//遍历删除
for (auto iter = vecS.begin(); iter != vecS.end(); ++iter) {
if (0 == iter->compare("four")) {
vecS.erase(iter--);//注意,删除时倒退
continue;
}
std::cout << *iter <<" ";
}
std::cout << std::endl;
//打印输出: one two five six
//复制
std::vector<std::string> vecCopy(vecS);
vecCopy.clear();
vecCopy.insert(vecCopy.begin(), vecS.begin(), vecS.end());
vecCopy.clear();
vecCopy = vecS;
//vecCopy.operator=(vecS);
vecCopy.clear();
vecCopy.assign(vecS.begin(), vecS.end());
if (vecCopy == vecS) {
std::cout << "same" << std::endl;
}
return 0;
}
/*打印输出:
two
one two three four five size seven
one two three four five size seven
one two three four five size seven
two five size
same
*/
移动语义支持
#include<iostream>
#include <vector>
#include <algorithm>
int main() {
class A {
int x = 0;
public:
A(int _x) : x(_x) {
std::cout << "A() : x=" << x << std::endl;
}
A(const A& a) {//拷贝构造函数
x = a.x;
std::cout << "A(const A& a) : x=" << x << std::endl;
}
A(A&& a) noexcept { //移动构造函数
x = a.x;
a.x = 0;
std::cout << "A(A&& a) : x=" << x << std::endl;
}
~A() {
std::cout << "~A() : x=" << x << std::endl;
}
A& operator=(const A& a) { //拷贝赋值运算符
if (this != &a) {
x = a.x;
}
std::cout << "operator=(const A& a) : x=" << x << std::endl;
return *this;
}
A& operator=(A&& a) noexcept { //移动赋值运算符
if (this != &a) {
x = a.x;
a.x = 0;
}
std::cout << "operator=(const A& a) : x=" << x << std::endl;
return *this;
}
void show() { std::cout << "x=" << x << std::endl; }
};
std::vector<A> vec;
//添加成员方法基本上都添加了移动语义支持
//void push_back(_Ty&& _Val)
//iterator insert(const_iterator _Where, _Ty&& _Val)
//decltype(auto) emplace_back(_Valty&&... _Val)
//iterator emplace(const_iterator _Where, _Valty&&... _Val)
//下面举例说明:
{//移动语义
A a(1);
vec.push_back(std::move(a));
vec.push_back(A(2));
//void push_back(_Ty&& _Val)
}
{//移动语义
vec.emplace_back(A(3));
vec.emplace_back(4);
//decltype(auto) emplace_back(_Valty&&... _Val)
}
{//移动语义
A a(5);
vec.insert(vec.end(), std::move(a));
vec.insert(vec.end(), A(6));
//iterator insert(const_iterator _Where, _Ty&& _Val)
}
{
A a(7);
vec.emplace(vec.end(), a);
vec.emplace(vec.end(), 8);
//iterator emplace(const_iterator _Where, _Valty&&... _Val)
}
{//通过移动转移
vec[vec.size() - 1].show(); //打印: 8
A a = std::move(vec[vec.size() - 1]);
vec[vec.size() - 1].show();//打印: 0
vec.pop_back();//转移之后,无效了,应该移除
}
{//通过移动转移
std::vector<A> vec1(std::move(vec));
std::vector<A> vec2 = std::move(vec1);
vec = std::move(vec2);
}
{//通过移动转移
//auto func = [](std::vector<A>&& a)->std::vector<A>&& { return std::forward<std::vector<A>>(a); };
auto func = [](std::vector<A>&& a) { return std::forward<std::vector<A>>(a); };
auto vec1 = func(std::forward<std::vector<A>>(vec));
vec = std::move(vec1);
}
return 0;
}
解析: 这段C++代码定义了一个名为A的类,该类有一个私有成员变量x,以及一些公共成员函数,如构造函数、拷贝构造函数、移动构造函数、析构函数、拷贝赋值运算符和移动赋值运算符。 演示std::vector的成员方法中关于移动语义的支持。移动语义是一种优化技术,它可以减少对象拷贝的开销,提高程序的性能。在C++11中引入了右值引用,使得移动语义成为可能。
首先,代码定义了一个名为A的类,该类有一个成员方法show(),用于打印对象的值。
然后,代码创建了一个std::vector<A>类型的变量vec,并使用push_back()方法向其中添加了几个A类型的对象。
接下来,代码演示了四种不同的方法来向vec中添加元素:
使用push_back()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。
使用emplace_back()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。
使用insert()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。
使用emplace()方法,并将参数作为右值传递。这种方法会自动调用A类的移动构造函数,从而实现移动语义。
最后,代码演示了如何通过移动语义将对象从一个vector转移到另一个vector中。
注意:在实际编程中,为了更好地利用移动语义,可以尽量使用右值引用作为函数参数,并在需要时使用std::forward()进行类型转换。
与智能指针结合
#include<iostream>
#include <vector>
#include <memory>
int main() {
class A {
int x = 0;
public:
A(int _x) : x(_x) {
std::cout << "A() : x=" << x << std::endl;
}
void show() { std::cout << "x=" << x << std::endl; }
};
{//vector 存放 shared_ptr
auto func = [](A* a) {
std::cout << "delete" << std::endl;
delete a;
};
std::vector<std::shared_ptr<A>> vec;
vec.push_back(std::shared_ptr<A>(new A(1), func));
vec.push_back(std::make_shared<A>(2));
vec.emplace(vec.end(), new A(3));
vec.emplace_back(new A(4));
vec.insert(vec.end(), { std::make_shared<A>(5), std::shared_ptr<A>(new A(6), func) });
while (!vec.empty()) {
vec.pop_back();
}
}
{//shared_ptr 包裹 std::vector
auto func = [](std::vector<A>* ptr) {
std::cout << "delete vector" << std::endl;
ptr->clear();
delete ptr;
};
std::shared_ptr<std::vector<A>> ptr = std::shared_ptr<std::vector<A>>(new std::vector<A>(), func);
ptr.reset();
}
return 0;
}
/*
第一个代码块:在这个代码块中,
我们创建一个 std::vector<std::shared_ptr<A>> 并向其中添加元素。
定义一个 lambda 函数 func,它接受一个指向 A 的指针,并删除它。
创建向量 vec,用于存放 std::shared_ptr<A>。
使用不同的方法向 vec 中添加元素:
使用 std::shared_ptr<A>(new A(1), func) 创建一个智能指针,并使用自定义删除器。
使用 std::make_shared<A>(2) 创建一个智能指针。
使用 vec.emplace(vec.end(), new A(3)) 在向量的末尾直接构造一个对象。
使用 vec.emplace_back(new A(4)) 在向量的末尾直接构造一个对象。
使用 vec.insert(vec.end(), { std::make_shared<A>(5),std::shared_ptr<A>(new A(6), func) })
在向量的末尾插入两个元素。
通过循环删除向量的所有元素,以展示自定义删除器的使用。
第二个代码块:在这个代码块中,我们使用 std::shared_ptr 来管理一个包含 A 对象的 std::vector。
定义另一个 lambda 函数 func,它接受一个指向 std::vector<A> 的指针,并清空向量并删除它。
创建 std::shared_ptr<std::vector<A>> ptr,并使用 lambda 函数作为删除器。
然后重置 ptr 以触发删除器。
*/
猜你喜欢
- 2024-11-11 Linux下的C++ socket编程实例 linux c++ tcp
- 2024-11-11 C++11原子变量:线程安全、无锁操作的实例解析
- 2024-11-11 C++11的thread_local原理和应用范例
- 2024-11-11 知识重构-c++ : Lambda 知识重构拼音
- 2024-11-11 深入探索C++异步编程的奥秘 c++11异步编程
- 2024-11-11 C++ 开发中使用协程需要注意的问题
- 2024-11-11 golang极速嵌入式Linux应用开发(四)-协程与并发
- 2024-11-11 在计算机编程中,线程是指一个程序内部的执行流程
- 2024-11-11 C++ std:decay、std:bind、std:packaged_task 在模版编程的实践
- 2024-11-11 Linux/C++简单线程池实现 了解Java语言对于多线程的支持多丰富
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)