容器适配器(container adapter)
注:博客由本人同步发布于 STL 容器适配器小结
内容主要总结自 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();