C++学习之路: STL探索 vector 和 list
引言: 这篇文章写在我们窥探庞大的STL 库之前,先熟悉一下基本操作,用于练手, 也用于过段时间的复习。
1.可以用一个容器去初始化另外一个容器。
但是两个容器的类型和内置类型都必须一致,否则编译无法通过。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 using namespace std; 5 6 //用一个容器去初始化另一个容器 7 int main(int argc, const char *argv[]) 8 { 9 vector<int> vec; 10 vec.push_back(12); 11 vec.push_back(89); 12 vec.push_back(34); 13 vec.push_back(23); 14 vec.push_back(56); 15 16 vector<int> vec2(vec); 17 for(int t : vec2) 18 { 19 cout << t << " "; 20 } 21 cout << endl; 22 23 return 0; 24 }
2.也可以用一段迭代器范围,去初始化另外一个容器:
同样迭代器也必须是该类型 的迭代器,这是基本要求,不要去尝试一些莫名其妙的初始化,例如用vector<string> 去初始化 vector<int> ,这种低级的错误
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 7 //用一段迭代器范围去初始化另一个容器 8 int main(int argc, const char *argv[]) 9 { 10 vector<int> vec; 11 vec.push_back(12); 12 vec.push_back(89); 13 vec.push_back(34); 14 vec.push_back(23); 15 vec.push_back(56); 16 17 vector<int>::iterator it1, it2; 18 it1 = vec.begin(); //12 19 it2 = find(vec.begin(), vec.end(), 23); 20 21 vector<int> vec2(it1, it2); //作为vec2的参数的两个迭代器it1和it2 也必须是vector<int>::iterator 类型,用vector<xxxxx> ::iterator 作为参数是不会通过编译的 22 for(int i : vec2) 23 { 24 cout << i << " "; 25 } 26 cout << endl; 27 return 0; 28 }
综上:我们在用容器,迭代器等方法去初始化另外一个容器,类型匹配是最基本的要求。
3.下面看一个错误的例子:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 7 //用一段迭代器范围去初始化另一个容器 8 int main(int argc, const char *argv[]) 9 { 10 vector<int> vec; 11 vec.push_back(12); 12 vec.push_back(89); 13 vec.push_back(34); 14 vec.push_back(23); 15 vec.push_back(56); 16 17 vector<double> vec2(vec); //vector<int> 与vector<double>类型不同 18 19 20 return 0; 21 }
上述代码,编译出现错误,因为vec2是一个vector<double> 类型,而vec是一个vector<int> 类型,两个容器类型不匹配。
4.容器类型不一样,无法使用一个容器初始化另外的方法,
却可以使用迭代器范围的方法,用一个容器的迭代器范围去初始化另外一个。 前提是它们的内置类型必须相近,如 int 和 double
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <algorithm> 5 using namespace std; 6 7 //用一段迭代器范围去初始化另一个容器 8 int main(int argc, const char *argv[]) 9 { 10 vector<int> vec; 11 vec.push_back(12); 12 vec.push_back(89); 13 vec.push_back(34); 14 vec.push_back(23); 15 vec.push_back(56); 16 17 vector<double> vec2(vec.begin(), vec.end()); 18 for(double d : vec2) 19 { 20 cout << d << " "; 21 } 22 cout << endl; 23 24 return 0; 25 }
上面代码所描述的情况就好比, 如果两个碗里面装的东西不一样的时候,它们是不能相互赋值(或初始化)的, 但是当它们*里面的东西*, 差不多的时候,可以用迭代器赋值。
如果差别太大,则编译不通过 如 迭代器指向的是string类型, 我们却用来初始化 一个vector<int> 容器,显然是不可取的。
5.当容器不一样,但是内置类型差不多的时候,也可以用迭代器的方法来初始化, 例如我们用vector<int>的 迭代器 来初始化list<int/double> 则是可以的
因为迭代器指向的内容和vector里面存放的东西差不多,就好像碗和碟子的关系,虽然容器不同,但是里面都是放的同一种食物(好比米饭), 我们就可以把碟子中的米饭,
复制到碟子里。
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <list> 5 #include <algorithm> 6 using namespace std; 7 8 //用一段迭代器范围去初始化另一个容器 9 int main(int argc, const char *argv[]) 10 { 11 vector<int> vec; 12 vec.push_back(12); 13 vec.push_back(89); 14 vec.push_back(34); 15 vec.push_back(23); 16 vec.push_back(56); 17 18 list<double> lst(vec.begin(), vec.end()); 19 for(double d : lst) 20 { 21 cout << d << " "; 22 } 23 cout << endl; 24 25 return 0; 26 }
6. 下面我们来看一看list,如果vector好比是数组,那么list就像链表一样,这个容器我们用的比较少。 看看它有哪些操作
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <list> 5 using namespace std; 6 7 int main(int argc, const char *argv[]) 8 { 9 list<string> lst; 10 lst.push_back("beijing"); 11 lst.push_back("shanghai"); 12 13 lst.push_front("shenzhen"); 14 15 for(list<string>::const_iterator it = lst.begin(); 16 it != lst.end(); 17 ++it) 18 { 19 cout << *it << " "; 20 } 21 22 cout << endl; 23 return 0; 24 }
list比vector 多出一个push_front成员函数,可以用于头插,把元素至于首POS。
vector为存储的对象分配一块连续的地址空间,因此对vector中的元素随机访问效率很高。在vecotor中插入或者删除某个元素,需要将现有元素进行复制,移动。如果vector中存储的对象很大,或者构造函数复杂,则在对现有元素进行拷贝时开销较大,因为拷贝对象要调用拷贝构造函数。对于简单的小对象,vector的效率优于list。vector在每次扩张容量的时候,将容量扩展2倍,这样对于小对象来说,效率是很高的。
list中的对象是离散存储的,随机访问某个元素需要遍历list。在list中插入元素,尤其是在首尾插入元素,效率很高,只需要改变元素的指针。
综上所述:
vector适用:对象数量变化少,简单对象,随机访问元素频繁
list适用:对象数量变化大,对象复杂,插入和删除频
vector 和 list 就好比 数组和链表一般, 一个是连续地址分布,一个是离散地址分布,
vector 可以高效 随机访问成员, 通过下标即可,但是插入和删除,需要移动其余的所以成员, 访问成员高效, 删除和插入低效
list 是插入删除 成员高效, 但是访问低效 ,因为每次访问都需要遍历 list 就好像链表。