zoukankan      html  css  js  c++  java
  • 从零开始学C++之模板(三):缺省模板参数(借助标准模板容器实现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;
    }





    为了支持  MyClass<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是指向这种类型的指

    针,编译通过。



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

    (一)、派生类与模板


    1、为了运行的效率,类模板是相互独立的,即独立设计,没有使用继承的思想。对类模板的扩展是采用适配器(adapter)来完成的。通用性是模板库的设计出发点之一,这是由泛型算法(algorithm)和函数对象(functor)等手段达到的。


    2、派生的目标之一也是代码的复用和程序的通用性,最典型的就是MFC,派生类的优点是可以由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务。


    3、模板追求的是运行效率,而派生追求的是编程的效率。


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

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


    面向对象

    动态多态(虚函数)

    泛型

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


    2、面向对象中的多态在运行时应用存在继承关系。我们编写使用这些类的代码,忽略基类与派生类之间的类型差异。只要使用基类指针或者引用,基类类型对象、派生类类型对象就可以共享相同的代码。


    3、在泛型编程中,我们所编写的类和函数能够多态地用于编译时不相关的类型。一个类或一个函数可以用来操纵多种类型的对象。


    参考:

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


  • 相关阅读:
    检查点学习笔记
    数据驱动-参数化(Parameters)
    loadrunner -vuser
    loadrunner 事务、同步点和思考时间
    loadrunner报错
    java随机数的产生
    Codeforces Round #666 (Div. 2) Power Sequence、Multiples of Length 思维
    Educational Codeforces Round 94 (Rated for Div. 2) String Similarity、RPG Protagonist、Binary String Reconstruction、Zigzags 思维
    Leetcode】周赛203 查找大小为M的最新分组
    HDU 6880 Permutation Counting dp
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3206620.html
Copyright © 2011-2022 走看看