zoukankan      html  css  js  c++  java
  • 缺省模板参数(借助标准模板容器实现Stack模板)、成员模板、关键字typename

    一、缺省模板参数

    回顾前面的文章,都是自己管理stack的内存,无论是链栈还是数组栈,能否借助标准模板容器管理呢?答案是肯定的,只需要多传一个模板参数即可,而且模板参数还可以是缺省的,如下:
    template <typename T, typename CONT = std::deque<T> >
    class Stack
    {

    private:

        CONT c_;
    };

    如果没有传第二个参数,默认为deque 双端队列,当然我们也可以传递std::vector<T>

    下面程序借助标准模板容器管理内存来实现stack模板类:

    Stack.h:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    #ifndef _STACK_H_
    #define _STACK_H_

    #include <exception>
    #include <deque>
    using namespace std;

    template <typename T, typename CONT = deque<T> >
    class Stack
    {
    public:
        Stack() : c_()
        {
        }
        ~Stack()
        {
        }

        void Push(const T &elem)
        {
            c_.push_back(elem);
        }
        void Pop()
        {
            c_.pop_back();
        }
        T &Top()
        {
            return c_.back();
        }
        const T &Top() const
        {
            return c_.back();
        }
        bool Empty() const
        {
            return c_.empty();
        }
    private:
        CONT c_;
    };

    #endif // _STACK_H_

    main.cpp:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    #include "Stack.h"
    #include <iostream>
    #include <vector>
    using namespace std;


    int main(void)
    {
        /*Stack<int> s;*/
        Stack<int, vector<int> > s;
        s.Push(1);
        s.Push(2);
        s.Push(3);

        while (!s.Empty())
        {
            cout << s.Top() << endl;
            s.Pop();
        }
        return 0;
    }

    输出为 3 2 1

    即如果没有传递第二个参数,堆栈和压栈等操作直接调用deque<int> 的成员函数,也由deque<int> 管理内存。

    如程序中传递vector<int> ,则由vector<int> 成员函数处理。


    二、成员模板

    来看下面的例子:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    #include <iostream>
    using namespace std;


    template <typename T>
    class MyClass
    {
    private:
        T value;
    public:
        void Assign(const MyClass<T> &x)
        {
            value = x.value;
        }
    };

    int main(void)
    {
        MyClass<double> d;
        MyClass<int> i;

        d.Assign(d);        // OK
        d.Assign(i);        // Error
        return 0;
    }

     
    因为i 和 d 的类型不同,故会编译出错。可以用成员模板的方法解决:
     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
     
    #include <iostream>
    using namespace std;

    template <typename T>
    class MyClass
    {
    private:
        T value;
    public:
        MyClass() {}
        template <class X>
        MyClass(const MyClass<X> &x) : value(x.GetValue())
        {

        }
        template <class X>
        void Assign(const MyClass<X> &x)
        {
            value = x.GetValue();
        }
        T GetValue() const
        {
            return value;
        }
    };

    int main(void)
    {
        MyClass<double> d;
        MyClass<int> i;
        d.Assign(d);        // OK
        d.Assign(i);        // OK

        MyClass<double> d2(i);

        return 0;
    }




    double> d2(i); 故也要将拷贝构造函数实现为成员模板函数,同理,如果想支持 d = i ; 也要将赋值运算符实现为成员
    实际上auto_ptr<class> 中的实现就使用了成员模板,因为要支持类似下面的运算:

    auto_ptr<X> x;

    auto_ptr<Y> y;

    x = y;


    三、typename 关键字

    看下面的例子:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    #include <iostream>
    using namespace std;

    template <typename T>
    class MyClass
    {
    private:
        typename T::SubType *ptr_;
    };

    class Test
    {
    public:
        typedef int SubType;
    };
    int main(void)
    {
        MyClass<Test> mc;
        return 0;
    }



    typename T::SubType *ptr_; 如果前面没有typename 修饰,则SubType会被认为是T类型内部的静态数据成员,推导下去,* 就不再认为是指针,而被

    认为是乘号,编译的时候就出错了。加上修饰,就知道SubType 是T 内部的自定义类型,ptr是指向这种类型的指针,编译通过。


    在vector 的源码中也可以发现下面的一些片段:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     

    template<> class _CRTIMP2_PURE allocator<void>
    {
        // generic allocator for type void
    public:
        template<class _Other>
        struct rebind
        {
            // convert an allocator<void> to an allocator <_Other>
            typedef allocator<_Other> other;
        };
        ....
    }

    typedef typename _Alloc::template rebind<_Ty>::other _Alty;

    最后一行是类型定义,由于要解释_Alloc 类型需要引用的代码片段比较多,就姑且认为是allocator<int> 类型,可以看到 rebind<_Ty>

     是成员模板类,other是成员模板类中自定义类型,_Ty 可以认为是int , 那么other 类型也就是allocator<int>, 也就是说_Alty 是类型

    allocator<int> 。

    此外还可以看到 :

    template<class _Ty> class allocator
     { 

    templateclassvoid
    };

    };

    也就是说allocator<void> 是allocator 模板类的特化。


    四、派生类与模板、面向对象与泛型编程

    (一)、派生类与模板

    1、为了运行的效率,类模板是相互独立的,即独立设计,没有使用继承的思想。对类模板的扩展是采用适配器(adapter)来完成的。通用性是模板库的设计出发点之一,这是由泛型算法(algorithm)和函数对象(functor)等手段达到的。
    2、派生的目标之一也是代码的复用和程序的通用性,最典型的就是MFC,派生类的优点是可以由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务。
    3、模板追求的是运行效率,而派生追求的是编程的效率。


    (二)、面向对象与泛型编程

    1、面向对象与泛型都依赖于某个形式的多态
    面向对象

    动态多态(虚函数)

    泛型

    静态多态(模板类,模板函数)

    2、面向对象中的多态在运行时应用存在继承关系。我们编写使用这些类的代码,忽略基类与派生类之间的类型差异。只要使用基类指针或者引用,基类类型对象、派生类类型对象就可以共享相同的代码。
    3、在泛型编程中,我们所编写的类和函数能够多态地用于编译时不相关的类型。一个类或一个函数可以用来操纵多种类型的对象。

    参考:

    C++ primer 第四版
    Effective C++ 3rd
    C++编程规范

  • 相关阅读:
    Java自学第18天
    Java自学第二十三天
    2\8定律,大家都要加油
    C#学习之我见
    2020年8月4日Java学习日记
    2020年8月5日
    2020年8月6日Java学习日记
    2020年8月7日Java学习日记
    第七章:(1)Spring Boot启动原理
    第六章:(3)数据访问 之 整合MyBatis
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8471401.html
Copyright © 2011-2022 走看看