《STL源码剖析》

第一章 STL概述

预计阅读时间10 分钟 114 views

可能令你困惑的C++语法

1.类里面的static成员

当使用模板类初始化多个实例时,这些实例共享这个模板(同类型)类的static成员。

#include <iostream>

template<typename T>
class myClass
{
public:
	static int i1_;
	static float f1_;
};

// int type template
int myClass<int>::i1_ = 1;
float myClass<int>::f1_ = 1.1f;

// float type template
int myClass<float>::i1_ = 2;
float myClass<float>::f1_ = 2.2f;

int main()
{
	myClass<int> ic1_, ic2_;
	myClass<float> fc1_, fc2_;

	std::cout
	    << ic1_.i1_ << ' '
		<< ic1_.f1_ << ' '
		<< ic2_.i1_ << ' '
		<< ic2_.f1_ << ' '
		<< fc1_.i1_ << ' '
		<< fc1_.f1_ << ' '
		<< fc2_.i1_ << ' '
		<< fc2_.f1_ << ' ';
	return 0;
}
C++

综上可知,当对ic1_实例的i1_成员进行操作时,i2_对应的成员也会发生变化, 因为它们使用的是同一个模板类myClass<int> ,同理,fc1_和fc2_也如此。

2.模板类的特殊设计

    初始化实例时,编译器会根据提供的指针的类型进行匹配。

    #include <iostream>
    
    // generalized
    template<class T, class O>
    struct tc
    {
    	tc() { std::cout << "I, O" << '\n'; }
    };
    
    // specialized
    template<class T>
    struct tc<T*, T*>
    {
    	tc() { std::cout << "T*, T*" << '\n'; }
    };
    
    // specialized
    template<class T>
    struct tc<const T*, T*>
    {
    	tc() { std::cout << "const T*, T*" << '\n'; }
    };
    
    int main()
    {
    	tc<int, char> obj1;
    	tc<int*, int*> obj2;
    	tc<const int*, int*> obj3;
    	tc<const int*, const int*> obj4;
    	tc<int*, const int*> obj5;
    	return 0;
    }
    C++

    输出结果:

    image_10.png

    若匹配不到,则会自动选择默认模板类。

    3.模板类中可再存在模板成员

      #include <iostream>
      
      class alloc{};
      
      template<class T, class Alloc = alloc>
      class vector{                       // template class
      public:
          typedef T value_type;           // T -> value_type
          typedef value_type* iterator;   // value_type* -> iterator
      
          template<class I>               // template member function
          void insert(iterator position, I first, I last){ std:: cout << "insert()" << '\n'; }
      };
      
      int main(){
          int ia[5] = {0, 1, 2, 3, 4};
      
          vector<int> x;
          vector<int>::iterator ite = nullptr;
          x.insert(ite, ia, ia + 5);
      
          return 0;
      }
      C++

      4.template 参数可以根据前一个 template 参数而设定默认值

        #include <iostream>
        #include <cstddef>
        
        class alloc {};
        
        template<class T, class Alloc = alloc, size_t BufSiz = 0>
        class deque{
        public:
            deque() { std::cout << "deque()" << '\n'; }
        };
        
        // 根据前一个参数值T, 设定下一个参数Sequence的默认值为deque<T>
        template<class T, class Sequence = deque<T>>
        class stack {
        public: stack() { std::cout << "stack()" << '\n'; }
        private: Sequence c;
        };
        
        int main() {
            stack<int> x;
            return 0;
        }
        C++

        输出结果:

        image_11.png

        由输出结果可以看出,在初始化stack实例时,stack内的Sequence会先根据模板内的参数初始化一个deque实例,再实例化stack。

        综上可知,当初始化一个模板类时,模板类中的模板中若有其他模板类,会优先初始其他化模板类对应的实例。

        示例1,如下所示:

        #include <iostream>
        #include <cstddef>
        
        class alloc {};
        
        template<class T, class Alloc = alloc, size_t BufSiz = 0>
        class deque{
        public:
            deque() { std::cout << "deque()" << '\n'; }
        };
        
        // 先初始化deque的实例
        template<class T, class Sequence = deque<T>>
        class stack {
        public: stack() { std::cout << "stack()" << '\n'; }
        private: Sequence c;
        };
        
        // 先初始化stack的实例
        template<class T, class Test = stack<T>>
        class test
        {
        public:
            test() { std::cout << "test()" << '\n'; }
        private:
            Test t;
        };
        
        int main() {
            test<int> t_;
            return 0;
        }
        C++

        输出结果:

        image_12.png

        示例2,如下所示

        #include <iostream>
        #include <cstddef>
        
        class alloc {};
        
        template<class T, class Alloc = alloc, size_t BufSiz = 0>
        class deque{
        public:
            deque() { std::cout << "deque()" << '\n'; }
        };
        
        template<class T, class Test = deque<T>>
        class test
        {
        public:
            test() { std::cout << "test()" << '\n'; }
        private:
            Test t;
        };
        
        template<class T, class Sequence = deque<T>, class test_ = test<T>>
        class stack {
        public: stack() { std::cout << "stack()" << '\n'; }
        private: Sequence c; test_ _t;
        };
        
        int main() {
            stack<int> t_;
            return 0;
        }
        C++

        结果如下:

        image_13.png

        过程推导:

        • 实例化deque -> deque()
        • 实例化test_, 但实例化test_前需实例化deque -> deque()
        • 实例化test_完成 -> test()
        • 实例化stack完成 -> stack()

        5.模板类可拥有Non-type的模板参数

          #include <iostream>
          #include <cstddef>
          
          class alloc {};
          
          inline size_t __deque_buf_size(size_t n, size_t sz) {
              return n != 0 ? n : (sz < 512) ? size_t(512 / sz) : size_t(1);
          }
          
          template<class T, class Ref, class Ptr, size_t BufSiz>
          struct __deque_iterator {
              typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
              typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
              static size_t buffer_size() { return __deque_buf_size(BufSiz, sizeof(T)); }
          };
          
          template<class T, class Alloc = alloc, size_t BufSiz = 0>
          class deque {
          public: typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
          };
          
          int main() {
              std::cout << deque<int>::iterator::buffer_size() << '\n';
              std::cout << deque<int, alloc, 64>::iterator::buffer_size() << '\n';
              return 0;
          }
          C++

          在类deque中的成员iterator初始化时利用模板中的参数BufSiz是一个value而不是一个type

          由此可知,模板接受非类型non-type的参数。

          6.Bound Friend Template

            类模板的某个具体实例(instantiation)与其友元函数模板的具体实例(instantiation)一一对应。

            #include <iostream>
            #include <cstddef>
            
            class alloc {};
            
            template<class T, class Alloc = alloc, size_t  BufSiz = 0>
            class deque{
            public: deque() { std::cout << "deque()" << '\n'; }
            };
            
            template<class T, class Sequence>
            class stack;
            
            template<class T, class Sequence>
            bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y);
            
            template<class T, class Sequence>
            bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y);
            
            template<class T, class Sequence = deque<T>>
            class stack{
                // style 1
                friend bool operator== <T> (const stack<T>& , const stack<T>& );
                friend bool operator< <T> (const stack<T>& , const stack<T>& );
                // style 2
                friend bool operator== <T> (const stack&, const stack&);
                friend bool operator< <T> (const stack&, const stack&);
                // style 3
                friend bool operator== <> (const stack&, const stack&);
                friend bool operator< <> (const stack&, const stack&);
                // invalid style
                //friend bool operator== (const stack&, const stack&);
                //friend bool operator< (const stack&, const stack&);
            public: stack() { std::cout << "stack()" << '\n'; }
            private: Sequence c;
            };
            
            template<class T, class Sequence>
            bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y){
                std::cout << "operator==()" << '\t';
                return true;
            }
            
            template<class T, class Sequence>
            bool operator< (const stack<T, Sequence>& x, const stack<T, Sequence>& y){
                std::cout << "operator<()" << '\t';
                return true;
            }
            
            int main(){
                stack<int> x;
                stack<int> y;
            
                std::cout << (x == y) << '\n';
                std::cout << (x < y) << '\n';
            
                //stack<int> y_;
                // invalid
                // std::cout << (x == y_) < '\n';   // no match for this
                // std::cout << (x < y_) << '\n';   // no match for this
            
                return 0;
            }
            C++

            一般步骤:在模板类内用友元函数声明要重载的运算符,(必须)再对声明的重载友元函数进行实现。

            输出结果:

            image_14.png

            7.类模板显式声明

              #include <iostream>
              
              #define __STL_TEMPLATE_NULL template<>
              
              template<class Key>
              struct hash{
                  void operator() () { std::cout << "hash<T>" << '\n'; }
              };
              
              // explicit specialization
              __STL_TEMPLATE_NULL struct hash<char>{
                  void operator() () { std::cout << "hash<char>" << '\n'; }
              };
              
              __STL_TEMPLATE_NULL struct hash<unsigned char>{
                  void operator() () { std::cout << "hash<unsigned char>" << '\n'; }
              };
              
              int main(){
                  hash<long> t1;
                  hash<char> t2;
                  hash<unsigned char> t3;
              
                  t1();
                  t2();
                  t3();
              
                  return 0;
              }
              C++

              输出结果:

              image_15.png

              hash<long> -> hash<T> ∵hash<long>并没有被显式声明,故调用默认模板
              
              hash<char> -> hash<char> ∵hash<char>已经被显式声明,故调用特定模板
              
              hash<unsigned char> -> hash<unsigned char> 原因同上
              C++

              通过这样的做法,可以自定义不同类型模板类的行为。

              8.临时对象的产生与运用

                #include <vector>
                #include <iostream>
                #include <algorithm>
                
                template<typename T>
                class print{
                public: void operator() (const T& elem) { std::cout << elem << ' '; }
                };
                
                int main(){
                    int ia[6] = {0, 1, 2, 3, 4, 5, 6};
                    std::vector<int> iv(ia, ia + 5);
                    // print<int> is an unnamed object but not a invoking operation
                    std::for_each(iv.begin(), iv.end(), print<int>());
                
                    return 0;
                }
                C++

                临时对象即无名对象(unnamed objects)。 实现方法

                对()符号进行重载: void operator() (const T& elem) { std::cout << elem << ' '; }

                不需声明指定对象名称,直接通过()进行调用std::for_each(iv.begin(), iv.end(), print<int>());

                此临时对象的生命周期

                状态动作
                开始for_each()(或其他函数)第一次调用print<int>()(临时对象)
                结束for_each()结束时

                9.静态常量成员在class内部直接初始化

                  #include <iostream>
                  
                  template<typename T>
                  class testClass{
                      public: expedient
                      static const int _datai = 5;
                      static const long _datal = 3l;
                      static const char _datac = 'c';
                  };
                  
                  int main(){
                      std::cout << testClass<int>::_datai << '\n';
                      std::cout << testClass<long>::_datal << '\n';
                      std::cout << testClass<char>::_datac << '\n;
                  
                      return 0;
                  }
                  C++

                  10.increment/decrement/dereference 操作符

                    此类操作符可用于迭代器(iterator)的指针的移动或取值(dereference)。

                    移动操作可分为前置(prefix)和后置(postfix。

                    #include <iostream>
                    
                    class INT{
                        friend std::ostream& operator<<(std::ostream& os, const INT& i);
                    public: INT(int i): m_i(i) {};
                    
                        // prefix: increment and then fetch
                        INT& operator++(){
                            ++(this->m_i);
                            return *this;
                        }
                    
                        // postfix: fetch and then increment
                        const INT operator++(int){
                            INT temp = *this;
                            ++(*this);
                            return temp;
                        }
                    
                        // prefix: minus and then fetch
                        INT& operator--(){
                            --(this->m_i);
                            return *this;
                        }
                    
                        // postfix: fetch and then increment
                        const INT operator--(int){
                            INT temp = *this;
                            --(*this);
                            return temp;
                        }
                    
                        // deference
                        int& operator*() const{
                            std::cout << "dereference" << '\n';
                            return (int&)m_i;
                        }
                    
                    private: int m_i;
                    };
                    
                    std::ostream& operator<<(std::ostream& os, const INT& i){
                        os << '[' << i.m_i << ']';
                        return os;
                    }
                    
                    int main(){
                        INT I(5);
                        std::cout << I++ << '\n';
                        std::cout << ++I << '\n';
                        std::cout << I-- << '\n';
                        std::cout << --I << '\n';
                        std::cout << *I << '\n';
                        return 0;
                    }
                    C++

                    输出结果:

                    image_16.png

                    分析:

                    • prefix increment :即前置++,类似地像++i这样的,优先对i进行操作后再使用。
                    // prefix: increment and then fetch
                       INT& operator++(){
                           ++(this->m_i);
                           return *this;
                       }
                    C++
                    • postfix increment :即后置++,类似地像i++这样的,优先使用i后,再对其进行操作。
                    // postfix: fetch and then increment
                      const INT operator++(int){
                          INT temp = *this;   // 声明一个临时对象temp用于存储要操作的目标对象,即还没被操作的目标对象
                          ++(*this);
                          return temp;        // 返回没有被操作的目标对象
                      }
                    C++
                    • dereference :即解引用,返回目标对象的对应类型的地址。
                    // deference
                    int& operator*() const{
                        std::cout << "dereference" << '\n';
                        return (int&)m_i;
                    }
                    C++

                    11.前闭后开区间表示法 [ )

                      template<class Iterator, class T>
                      InputIterator find(Iterator first, Iterator Last, const T& value){
                          while(first != last & *first != value) ++first;
                          return first;
                      }
                      
                      template<class InputIterator, class Function>
                      Function for_each(InputIterator first, InputIterator last, Function f){
                          for(; first != last; ++first) f(*first)
                          return f;
                      }
                      C++

                      前闭后开即 [first, last), 让指针从first开始 而达不到last(last的前一个位置 => last - 1)。

                      image_17.png

                      12.function call操作符(operator())

                      #include <iostream>
                      
                      template<class T>
                      struct plus{
                          T operator() (const T& x, const T& y) const { return x + y; }
                      };
                      
                      template<class T>
                      struct minus{
                          T operator() (const T& x, const T& y) const { return x - y; }
                      };
                      
                      int main(){
                          plus<int> plusobj;      // functor object of plus
                          minus<int> minusobj;    // functor object of minus
                      
                          // invoke the functor by the corresponding instances
                          std::cout << plusobj(5, 10) << '\n';
                          std::cout << minusobj(10, 5) << '\n';
                      
                          // create the temporary object as the functor
                          std::cout << plus<int>()(30, 20) << '\n';
                          std::cout << minus<int>()(210, 10) << '\n';
                      
                          return 0;
                      }
                      C++

                      此处对各模板类的 () 进行了重载,使之成为了一个仿函数(functor)

                      Leave a Comment

                      Share this Doc

                      第一章 STL概述

                      Or copy link

                      CONTENTS
                      It's late! Remember to rest.