第一章 STL概述

可能令你困惑的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;
}

综上可知,当对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;
}

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

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;
}

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;
}

由输出结果可以看出,在初始化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;
}

示例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;
}

结果如下:

过程推导:

  • 实例化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;
}

在类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;
}

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

输出结果:

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;
}

输出结果:

hash<long> -> hash<T> ∵hash<long>并没有被显式声明,故调用默认模板

hash<char> -> hash<char> ∵hash<char>已经被显式声明,故调用特定模板

hash<unsigned char> -> hash<unsigned char> 原因同上

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

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;
}

临时对象即无名对象(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;
}

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;
}

输出结果:

分析:

  • prefix increment:即前置++,类似地像++i这样的,优先对i进行操作后再使用。

    // prefix: increment and then fetch
    INT& operator++(){
        ++(this->m_i);
        return *this;
    }
  • postfix increment:即后置++,类似地像i++这样的,优先使用i后,再对其进行操作。

    // postfix: fetch and then increment
    const INT operator++(int){
        INT temp = *this;   // 声明一个临时对象temp用于存储要操作的目标对象,即还没被操作的目标对象
        ++(*this);
        return temp;        // 返回没有被操作的目标对象
    }
  • dereference:即解引用,返回目标对象的对应类型的地址。

    // deference
    int& operator*() const{
    std::cout << "dereference" << '\n';
    return (int&)m_i;
    }

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;
}

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

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;
}

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

Related Post

STL源码分析-第二章

第二章 空间配置器(allocator) 1.空间配置器...

0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
It's late! Remember to rest.
0
希望看到您的想法,请您发表评论x