C++标准库还提供了一些特殊的容器类型,所谓的容器适配器(container adapter,包括stack、queue、priority-queue)以及bitset、valarray。这些容器都有特殊的接口,都不满足STL容器的一般要求。
1.容器的共通能力和共通操作
3个核心的共通能力:
#所有容器提供的都是value语意而不是Reference语意,因此容器内的元素都可以被拷贝
#总而言之,所有元素形成一个次序order:迭代器可以按照这个order遍历每一个元素,而每个容器都提供可返回迭代器的函数。使用迭代器遍历元素是STL算法赖以生存的关键接口。
#各种操作并非绝对安全,STL不会主动抛出异常
共通操作:
#初始化initialization:
每个容器都提供了一个默认构造函数、一个copy构造函数、一个析构函数。
可以以已知区间的内容初始化容器,这样的构造函数一般是member template。
@以另一个容器的元素初始化
std::list<int> l;
std::vector<float> c(l.begin(),c.end());
@以数组初始化
int array[]= {}
std::set<int> s(array,array + sizeof(array)/sizeof(array[0]));
@以标准输入完成初始化
std::deque<int> c(std::istream_iterator<int>(std::cin),std::istream_iterator<int>());
所有容器都提供了三个和大小相关的函数:
size() empty() max_size()
比较函数:operator
assignment函数和swap():容器的赋值函数效率很低,swap性能较好
2.vector
vector模型出了一个动态数组的抽象概念。
定义:
namespace std{
template<class T,class Allocator = allocator<T>> class vector;
第二个template参数可有可无,用来定义内存模型,缺省的内存模型为C++标准库提供的allocator。
能力:
vector是一个有序群集ordered collection,支持随机存取,根据位置在常数时间内存取一个元素,它的迭代器也是随机存取迭代器。
在末端添加和删除元素效率较高,每一个移动元素都调用一次operator assignment。
vector优异性能的秘诀是配置了比所容纳的元素更多的内存。
capacity函数返回vector所能容纳的函数的元素数量,如果超过了这个数量,那么vector需要重新配置内部存储器,这样vector相关的reference、pointer、iterator都会失效。
内部存储器的重新配置是十分浪费时间的,要尽量避免重新的内存配置:
#reserve函数保留适当容量
std::vector<int> v;
v.reverse(80); //保留了80的额外容量
#初始化时向构造函数传递附加参数
std::vector<int> v(20); //必须提供一个默认的构造函数
vector不能使用reserve()缩减容量。
vector缩减容量的小窍门:
template <class T>
void shrinkCapacity(std::vector<T>& V){
std::vector<T> temp(V);
V.swap(temp);
}
vector的构造函数和析构函数:
vector<T> v; vector<T> v(v1); vector<T> v(n);
vector<T> v(n,elem); vector<T> v(beg,end); ~vector<T>;
vector的非变动性操作:
c.size(); c.max_size(); c.empty(); c.capacity(); c.reserve();
c1 == c2; c1 < c2; c1 <= c2; c1 > c2; c1 >= c2; c1 != c2;
vector的赋值操作:
c1 = c2; c.assign(n,elem); c.assign(beg,end);
c1.swap(c2); swap(c1,c2);
vector的随机存取操作:
c.at(idx); c[idx]; c.front(); c.back();
只有at函数会做越界检查,并抛出out_of_range异常。
vector的迭代器函数: random access iterator
提供了一些常规函数获得一般迭代器,一般指针就是随机存取迭代器。
c.begin(); c.end(); c.rbegin(); c.rend();
vector的安插和移除操作:
按照STL惯例,需要保证入参的合法性:迭代器必须指向一个合法位置,指示区间迭代器合法,决不能从空容器中做移除操作。
c.insert(pos,elem); c.insert(pos,n,elem); c.insert(pos,beg,end);
c.push_back(elme); c.pop_back();
c.erase(pos); c.erase(beg,end);
c.resize(num); c.resize(num,elem);
c.clear();
vector中元素的删除:
显然,vector并没有提供直接删除“值等于某个值”的所有元素的算法,这是算法发挥威力:
#多个元素的删除: 获得一个待删除区间
std::vector<elem> col;
col.erase(remove(col.begin(),col.end(),val),col.end());
回忆:remove(col.begin(),col.end(),val); ”删除“区间范围内所有值为val的元素,它并没有真正地删除,在容器终点留下n个冗余元素,n代表着删除的元素
返回新的容器终点,和erase()配合使用,
col.erase(remove(col.begin(),col.end(),val),col.end());那么将新的终点和老的终点间的冗余元素删除。
#单个元素的删除: 获得一个待删除的position
pos = find(col.begin(),col.end(),val);
col.erase(pos);
将vector当做array来使用的:
std::vector <char> v;
v.resize(41);
strcpy(&v[0],"welcome to myblog"); //ok
strcpy(v.begin(),"welcome to bupt"); //error,begin()返回的往往不是一个普通的指针
printf("%s",&v[0]);
vector只提供最低限度的逻辑错误检查,只抛出一般标准异常和用户自定义的异常。
class vector<bool>是C++标准库专门为bool的vector设计的一个特殊版本:
一般的vector为每一个元素分配一个byte的空间,而class vector<bool>只为其中元素分配一个bit的空间。
因此,它的操作也是基于bit的,提供特殊的bit操作。常被当做动态的bitfield使用,静态的bitfield直接使用bitset就行。
class vector<bool>特殊操作:
v.flip() //所有元素取反
v[idx].flip(); v[idx] = val; v[idex1] = v[idex2];
vector<bool>中的常见技巧proxy:
对vector<bool>使用下标,返回的是一个bool,而调用flip()是bit操作。
实际上下标操作符返回的实际上是一个辅助类型,一旦你要求返回值是一个bool,就会触发一个自动类型转换函数。
proxy让你控制一般无法控制的东西,通常用来获取更好的安全性。
namespace std{
class vector<bool>{
public:
//auxiliary type for subscript operater
class reference{
public:
//automatic type conversion to bool
operator bool() const;
reference& operater=(const bool);
reference& operater=(const reference&);
void flip();
}
reference operator[](size_type n);
reference at(size_type n);
reference front();
reference back();
}
上面的operator bool() const;有点让人疑惑:
看看英文的解释:
Member functions of the form
operator TypeName() are conversion operators.
They allow objects of the class type to be used as if they were of type TypeName and when they are, they are converted to TypeName using the conversion function.
In this particular case, operator bool() allows an object of the class type to be used as if it were a bool.
For example, if you have an object of the class type named obj, you can use it as
if (obj)
This will call the operator bool(), return the result, and use the result as the condition of the if.
It should be noted that operator bool() is A Very Bad Idea and you should really never use it. For a detailed explanation as to why it is bad and for the solution to the problem, see "The Safe Bool Idiom."
简单的意思就是:
operator TypeName()是类型转化符,它允许一个类的对象可以像TypeName类一样被使用。
operator bool()就允许将reference类作为bool型使用,就可以使用一个reference的对象,如下:
vector<bool> v; if(v.back()){}; //因为v.back()返回一个reference类
但是在The Safe Bool Idiom中他认为operator bool()是个bad idea,应该弃用。
在本例中,对它进行[]运算,返回一个reference代理类,代理类可以转换为bool类型使用,同时可以通过代理类来设置vector的某个元素。
这样意味着每一个调用下标取值或者at,那么就会有一次reference类的实例化,在退出时有类的析构。
1.容器的共通能力和共通操作
3个核心的共通能力:
#所有容器提供的都是value语意而不是Reference语意,因此容器内的元素都可以被拷贝
#总而言之,所有元素形成一个次序order:迭代器可以按照这个order遍历每一个元素,而每个容器都提供可返回迭代器的函数。使用迭代器遍历元素是STL算法赖以生存的关键接口。
#各种操作并非绝对安全,STL不会主动抛出异常
共通操作:
#初始化initialization:
每个容器都提供了一个默认构造函数、一个copy构造函数、一个析构函数。
可以以已知区间的内容初始化容器,这样的构造函数一般是member template。
@以另一个容器的元素初始化
std::list<int> l;
std::vector<float> c(l.begin(),c.end());
@以数组初始化
int array[]= {}
std::set<int> s(array,array + sizeof(array)/sizeof(array[0]));
@以标准输入完成初始化
std::deque<int> c(std::istream_iterator<int>(std::cin),std::istream_iterator<int>());
所有容器都提供了三个和大小相关的函数:
size() empty() max_size()
比较函数:operator
assignment函数和swap():容器的赋值函数效率很低,swap性能较好
2.vector
vector模型出了一个动态数组的抽象概念。
定义:
namespace std{
template<class T,class Allocator = allocator<T>> class vector;
第二个template参数可有可无,用来定义内存模型,缺省的内存模型为C++标准库提供的allocator。
能力:
vector是一个有序群集ordered collection,支持随机存取,根据位置在常数时间内存取一个元素,它的迭代器也是随机存取迭代器。
在末端添加和删除元素效率较高,每一个移动元素都调用一次operator assignment。
vector优异性能的秘诀是配置了比所容纳的元素更多的内存。
capacity函数返回vector所能容纳的函数的元素数量,如果超过了这个数量,那么vector需要重新配置内部存储器,这样vector相关的reference、pointer、iterator都会失效。
内部存储器的重新配置是十分浪费时间的,要尽量避免重新的内存配置:
#reserve函数保留适当容量
std::vector<int> v;
v.reverse(80); //保留了80的额外容量
#初始化时向构造函数传递附加参数
std::vector<int> v(20); //必须提供一个默认的构造函数
vector不能使用reserve()缩减容量。
vector缩减容量的小窍门:
template <class T>
void shrinkCapacity(std::vector<T>& V){
std::vector<T> temp(V);
V.swap(temp);
}
vector的构造函数和析构函数:
vector<T> v; vector<T> v(v1); vector<T> v(n);
vector<T> v(n,elem); vector<T> v(beg,end); ~vector<T>;
vector的非变动性操作:
c.size(); c.max_size(); c.empty(); c.capacity(); c.reserve();
c1 == c2; c1 < c2; c1 <= c2; c1 > c2; c1 >= c2; c1 != c2;
vector的赋值操作:
c1 = c2; c.assign(n,elem); c.assign(beg,end);
c1.swap(c2); swap(c1,c2);
vector的随机存取操作:
c.at(idx); c[idx]; c.front(); c.back();
只有at函数会做越界检查,并抛出out_of_range异常。
vector的迭代器函数: random access iterator
提供了一些常规函数获得一般迭代器,一般指针就是随机存取迭代器。
c.begin(); c.end(); c.rbegin(); c.rend();
vector的安插和移除操作:
按照STL惯例,需要保证入参的合法性:迭代器必须指向一个合法位置,指示区间迭代器合法,决不能从空容器中做移除操作。
c.insert(pos,elem); c.insert(pos,n,elem); c.insert(pos,beg,end);
c.push_back(elme); c.pop_back();
c.erase(pos); c.erase(beg,end);
c.resize(num); c.resize(num,elem);
c.clear();
vector中元素的删除:
显然,vector并没有提供直接删除“值等于某个值”的所有元素的算法,这是算法发挥威力:
#多个元素的删除: 获得一个待删除区间
std::vector<elem> col;
col.erase(remove(col.begin(),col.end(),val),col.end());
回忆:remove(col.begin(),col.end(),val); ”删除“区间范围内所有值为val的元素,它并没有真正地删除,在容器终点留下n个冗余元素,n代表着删除的元素
返回新的容器终点,和erase()配合使用,
col.erase(remove(col.begin(),col.end(),val),col.end());那么将新的终点和老的终点间的冗余元素删除。
#单个元素的删除: 获得一个待删除的position
pos = find(col.begin(),col.end(),val);
col.erase(pos);
将vector当做array来使用的:
std::vector <char> v;
v.resize(41);
strcpy(&v[0],"welcome to myblog"); //ok
strcpy(v.begin(),"welcome to bupt"); //error,begin()返回的往往不是一个普通的指针
printf("%s",&v[0]);
vector只提供最低限度的逻辑错误检查,只抛出一般标准异常和用户自定义的异常。
class vector<bool>是C++标准库专门为bool的vector设计的一个特殊版本:
一般的vector为每一个元素分配一个byte的空间,而class vector<bool>只为其中元素分配一个bit的空间。
因此,它的操作也是基于bit的,提供特殊的bit操作。常被当做动态的bitfield使用,静态的bitfield直接使用bitset就行。
class vector<bool>特殊操作:
v.flip() //所有元素取反
v[idx].flip(); v[idx] = val; v[idex1] = v[idex2];
vector<bool>中的常见技巧proxy:
对vector<bool>使用下标,返回的是一个bool,而调用flip()是bit操作。
实际上下标操作符返回的实际上是一个辅助类型,一旦你要求返回值是一个bool,就会触发一个自动类型转换函数。
proxy让你控制一般无法控制的东西,通常用来获取更好的安全性。
namespace std{
class vector<bool>{
public:
//auxiliary type for subscript operater
class reference{
public:
//automatic type conversion to bool
operator bool() const;
reference& operater=(const bool);
reference& operater=(const reference&);
void flip();
}
reference operator[](size_type n);
reference at(size_type n);
reference front();
reference back();
}
上面的operator bool() const;有点让人疑惑:
看看英文的解释:
Member functions of the form
operator TypeName() are conversion operators.
They allow objects of the class type to be used as if they were of type TypeName and when they are, they are converted to TypeName using the conversion function.
In this particular case, operator bool() allows an object of the class type to be used as if it were a bool.
For example, if you have an object of the class type named obj, you can use it as
if (obj)
This will call the operator bool(), return the result, and use the result as the condition of the if.
It should be noted that operator bool() is A Very Bad Idea and you should really never use it. For a detailed explanation as to why it is bad and for the solution to the problem, see "The Safe Bool Idiom."
简单的意思就是:
operator TypeName()是类型转化符,它允许一个类的对象可以像TypeName类一样被使用。
operator bool()就允许将reference类作为bool型使用,就可以使用一个reference的对象,如下:
vector<bool> v; if(v.back()){}; //因为v.back()返回一个reference类
但是在The Safe Bool Idiom中他认为operator bool()是个bad idea,应该弃用。
在本例中,对它进行[]运算,返回一个reference代理类,代理类可以转换为bool类型使用,同时可以通过代理类来设置vector的某个元素。
这样意味着每一个调用下标取值或者at,那么就会有一次reference类的实例化,在退出时有类的析构。