STL 容器适配器小结

容器适配器(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();
posted @ 2020-06-08 16:52  icky_1024  阅读(305)  评论(0编辑  收藏  举报
Live2D