容器适配器(container adapter)
注:内容主要总结自 C++ Standard Library 第二版。
C++ 标准库还包含一些满足特殊需求而设计的容器,它们提供非常简单的接口。这些容器被归类为容器适配器,它们是由标准 STL 容器构建的。有三种标准的容器适配器:
- stack
- queue
- priority_queue
还有一种特殊的容器适配器:
- bitset
1.1 stack(堆栈)
位于头文件 stack 中,定义如下:
namespace std { template <class T, class Container = deque<T> > class stack; }
- 第一个 template 参数代表元素类型
- 带有默认值的第二个 template 参数用来定义 stack 内部用来存放元素的实际容器,默认为 deque
核心接口
- push ()
- pop ()
- top ()
详细定义
namespace std { template <class T, class Container = deque<T> > class stack { public: typedef typename Container::value_type value_type; typedef typename Container::size_type size_type; typedef Container container_type; protected: Container c; // container public: explicit stack(const Container& = Container()); bool empty() const { return c.empty(); } size_type size() const { return c.size(); } void push (const value_type& x) { c.push_back(x); } void pop() { c.pop_back(); } value_type& top() { return c.back(); } const value_type& top() const { return c.back(); } }; template <class T, class Container> bool operator==(const stack<T, Container>&, const stack<T, Container>&); template <class T, class Container> bool operator< (const stack<T, Container>&, const stack<T, Container>&); ...// (other comparison operators)
}
从 protect 区域可以看出,类内部仅有一个 Container 类型的对象 c。仅适用该对象便可实现 stack <>。
实际上我们也可以适用类似的方法(composition,组合)的方式,来通过 STL 容器实现自己的数据结构。
1.2 queue(队列)
位于头文件 queue 中,class queue 定义如下:
namespace std { template <class T, class Container = deque<T> > class queue; }
- 第一个 template 参数代表元素类型
- 带有默认值的第二个 template 参数用来定义 queue 内部用来存放元素的实际容器,默认为 deque
核心接口
实际上只要容器有 push_back ()、front ()、back ()、pop_front ()操作就可以作为 queue 的容器,在 queue 内部只是把这些操作转化而已:
- push () -- push_back ()
- front () -- front ()
- back () -- back ()
- pop () -- pop_front ()
详细定义
namespace std { template <class T, class Container = deque<T> > class queue { public: typedef typename Container::value_type value_type; typedef typename Container::size_type size_type; typedef Container container_type; protected: Container c; // container public: explicit queue(const Container& = Container()); bool empty() const { return c.empty(); } size_type size() const { return c.size(); } void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_front(); } value_type& front() { return c.front(); } const value_type& front()const { return c.front(); } value_type& back() { return c.back(); } const value_type& back() const { return c.back(); } }; template <class T, class Container> bool operator==(const queue<T, Container>&, const queue<T, Container>&); template <class T, class Container> bool operator< (const queue<T, Container>&, const queue<T, Container>&); //(other comparison operators) }
1.3 priority_queue(优先队列)
位于头文件 queue 中,定义如下:
namespace std { template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue; }
- 第一个 template 参数代表元素类型
- 带有默认值的第二个 template 参数用来定义 priority_queue 内部用来存放元素的实际容器,默认为 vector
- 带有默认值的第三个 template 参数定义“用来查找下一个最高优先级元素”的排序准则,默认以 < 作为比较标准(大顶堆)
核心接口
- push ()
- top ()
- pop ()
只要容器支持 random-access iterator 和 front ()、push_back ()、pop_back (),就可以作为 priority_queue 的容器。由于 priority_queue 要用到 STL heap 算法,所以容器必须支持 random-access iterator。
详细定义
namespace std { template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue { public: typedef typename Container::value_type value_type; typedef typename Container::size_type size_type; typedef Container container_type; protected: Compare comp; // sorting criterion Container c; // container public: // constructors explicit priority_queue(const Compare& cmp = Compare(), const Container& cont = Container()) : comp(cmp), c(cont) { make_heap(c.begin(),c.end(),comp); } template <class InputIterator> priority_queue(InputIterator first, InputIterator last, const Compare& cmp = Compare(), const Container& cont = Container()) : comp(cmp), c(cont) { c.insert(c.end(),first,last); make_heap(c.begin(),c.end(),comp); } void push(const value_type& x); { c.push_back(x); push_heap(c.begin(),c.end(),comp); } void pop() { pop_heap(c.begin(),c.end(),comp); c.pop_back(); } bool empty() const { return c.empty(); } size_type size() const { return c.size(); } const value_type& top() const { return c.front(); } }; }
从 protected 区域可以看出, priority_queue 类仅有成员 Container 对象和 Compare 比较函数。它的内部操作实际上转化为 STL heap 算法的操作,初始化、pop、push 等操作都在容器内部操作,随后调用 make_heap 算法建立或者调整堆。
1.4 bitset
定义于头文件 bitset 中:
namespace std { template <size_t Bits> class bitset; }
只有一个 template 参数用以指定 bit 的数量。注意该参数实际上是一个不带正负号的正数,而不是一个类型。
常用接口
operator [] | 存取某特定位置的位 |
test () | 返回bool,标志某特定位置的位 |
all | 检查是否 all、any、none的位被设置为 true |
any | |
none | |
count | 返回被设置为 true 的位的个数 |
size | 返回该 bitset 所能容纳的位数 |
set | 设置某位为 true |
reset | 设置某位为 false |
flip | 设置某位为 !bit ,对该位值取非 |
to_string () | 转换为 string |
to_ulong () | 转换为 unsigned long |
to_ullong () | 转换为 unsigned long long |
#include <iostream> #include <bitset> #include <limits> #include <map> enum Color { red, yellow, green, blue, white, black, numColors }; int main() { using namespace std; map<Color, string> mp; mp[red] = "red"; mp[yellow] = "yellow"; mp[green] = "green"; mp[blue] = "blue"; mp[white] = "white"; mp[black] = "black"; // create bitsetfor all bits/colors bitset<numColors> usedColors; //bitset<numColors> usedColors;使用该语句效果一样,因为枚举类型 numColors 对应的值为6 // set bits for two colors usedColors.set(red); usedColors.set(blue); // print some bitset data cout << "bitfield of used colors: " << usedColors << endl; cout << "number of used colors: " << usedColors.count() << endl; cout << "bitfield of unused colors: " << ~usedColors << endl; // if any color is used if (usedColors.any()) { // loop over all colors for (int c = 0; c < numColors; ++c) { // if the actual color is used if (usedColors[(Color)c]) { cout << "color: " << mp[(Color)c] << " was used!" << endl; //此处 mp[key] 中的key必须转换为Color枚举类型,而不能期望自动转换 } } } usedColors[red] = false; cout << "change red to unused: " << usedColors.test(red) << endl; usedColors.flip(red); cout << "change red to used: " << usedColors.test(red) << endl; usedColors.reset(red); cout << "change red to unused: " << usedColors.test(red) << endl; bitset<numeric_limits<unsigned short >::digits> bits(128); cout << "use numeric_limits<T>::digits : " << bits << endl; //cout << bitset<numeric_limits<unsigned short>::digits>(128) << endl;两者效果等同 string s = bits.to_string(); //必须使用成员函数 to_string 而不是 std::to_string() cout << "bits to string: " << s << endl; unsigned long exchanged_val = bitset<64>(s).to_ulong(); unsigned long val = bits.to_ulong(); cout << "bitset to long: " << val << endl; cout << "string to bitset to unsigned long:" << exchanged_val <<endl; return 0; }
输出结果:
bitfield of used colors: 001001 number of used colors: 2 bitfield of unused colors: 110110 color: red was used! color: blue was used! change red to unused: 0 change red to used: 1 change red to unused: 0 use numeric_limits<T>::digits : 0000000010000000 bits to string: 0000000010000000 bitset to long: 128 string to bitset to unsigned long:128
用 bitset 表述二进制
#include <bitset> #include <iostream> #include <string> #include <limits> using namespace std; int main() { /* print some numbers in binary representation */ cout << "267 as binary short: " << bitset<numeric_limits<unsigned short>::digits>(267) << endl; cout << "267 as binary long: " << bitset<numeric_limits<unsigned long>::digits>(267) << endl; cout << "10,000,000 with 24 bits: " << bitset<24>(1e7) << endl; /* transform binary representation into integral number */ cout << ""1000101011" as number: " << bitset<100>(string("1000101011")).to_ulong() << endl; }
输出如下(取决于机器 short 和 long 的位数):
267 as binary short: 0000000100001011 267 as binary long: 00000000000000000000000100001011 10,000,000 with 24 bits: 100110001001011010000000 "1000101011" as number: 555
操作符 << 针对bitset 的特别设计,允许将一个 bitset 打印为一个二进制串。如果需要存储为字符串,需要使用 bitset 提供的成员函数 to_string (),如:
string s = bitset<32>(1234567).to_string();
同样,也可以把字符串转换为一个 bitset:
bitset<64>("10010010");
然后调用 bitset 的成员函数 to_ullong (),使字符串转换为一个整数值:
bitset<64>("10010010").to_ullong();