zoukankan      html  css  js  c++  java
  • 我也来谈一谈c++模板(一)

        c++中程序员使用模板能够写出与类型无关的代码,提高源代码重用,使用合适,大大提高了开发效率。此前,可以使用宏实现模板的功能,但是模板更加安全、清晰。在编写模板相关的代码是我们用到两个关键词:template和class(或者typename),class和typename基本没有区别。

    1、函数模板

         函数模板本质上就是实现针对不同类型的同一种算法的代码,其基本用法可以是形如:

     template <class TYPE ,class TYPE,......>

     ReturnValue function(Argments)

     {

     // code

     }

         这里template <class TYPE ,class TYPE,......> 声明了此段代码是一段模板。”<>“中提供的是模板的类型参数,当使用这段代码时,编译器会根据使用的实际类型产生一份实例。在接下来的整个函数模板中就可以使用类型参数了,函数的returnvalue,arguments,local variable都可以使用类型参数。下面是一段简单的函数模板使用实例:

     1 template <class T> T foo(T& lhs,T& rhs)
     2 {
     3     T tmp = lhs;
     4     lhs = rhs;
     5     rhs = tmp;
     6     return lhs>rhs?lhs:rhs;
     7 }
     8 
     9 int main()
    10 {
    11     int a(5),b(10);
    12     cout << foo(a,b) << endl;
    13     cout << "a=" << a << endl;
    14     cout << "b=" << b << endl; 
    15 
    16     string c("hello"),d("world");
    17     cout << foo(c,d) << endl;
    18     cout << "c=" << c << endl;
    19     cout << "d=" << d << endl;
    20 }

        1-5行就是函数模板的基本使用方式,foo()函数的功能是交换两个参数,然后返回较大的值,但是foo()函数并不关心参数的类型 。当我们在main()中使用foo模板是,分别使用int和string类型都能得到期望的结果,而foo的代码只写了一份,没有针对int和string。

    这是输出结果:

    10
    a=10
    b=5
    world
    c=world
    d=hello

    2、类中的函数模板

        类中含有成员函数模板的情况和上一种情况类似,只是限定了一下作用域。说实话,这一种情况应该比上一种情况更加常见,因为c++是一门面向对象的语言,我们定义的函数,通常需要定义在类中,尽量减少全局函数的使用。当然有点扯远了,写个小例子,说明用法:

    class X
    {
    public:
       template <class T> 
      void mf(T* t) {} }; int main() { int i; X* x = new X(); x->mf(&i); }

        基本和第一种情况一样,不再赘述了。

    3、简单的类模板

        定义类模板的语法通项可以是这样的公式:

    template<class TYPE,class TYPE...>

    class CLASSNAME

    {

      //members.

    };

    template<class TYPE,class TYPE...>

    ReturnValue CLASSNAME<TYPE,TYPE...>::function (Arguments)

    {

      //code.

    }

        类模板的使用大体和函数模板类似,只是要注意一点,当声明和定义相分离时,定义成员函数的时候,光限定模板名是不够的,要加上类型,只有这样限定才会是一个类名而非模板。举个例子:

    template <class T>
    class Bar {
        void foo();
    };
    
    template<class t>
    void Bar<T>::foo()
    {
        //
    }

    void Bar<T>::foo()是重要的,这是使用模板最常见的错误之一了。Bar只是模板而Bar<T>才是类名。

    4、类模板中的函数模板

        类中的成员函数也可以是一个模板,这样就形成了一种层次关系。需要注意的是,在定义成员函数函数模板的时候需要在上例的基础上,考虑模板间的层次关系。例如这样一段代码:

     1 template<typename T>
     2 class X
     3 {
     4 public:
     5    template<typename U>
     6    void mf(const U &u);
     7 };
     8 
     9 template<typename T> 
    10 template <typename U>
    11 void X<T>::mf(const U &u)
    12 {
    13 }
    14 
    15 int main()
    16 {
    17 }

      9-10行中限定了两个类型参数T、U的层次,其中U是成员函数函数模板的类型参数,如果11行这样写:

    void X<U>::mf(const T &u)

       就会报错。

    还有一点需要注意的是:

    Member template functions cannot be virtual functions and cannot override virtual functions from a base class when they are declared with the same name as a base class virtual function.

    当使用与基类虚函数相同的名称进行声明时,成员模板函数不能是虚函数并且不能从基类重写虚函数。

       关于之间的区别还是写两段代码具体的感受一下比较好。

    5、在普通类中嵌套类模板

        嵌套类模板被声明为外部类范围内的类模板,可以在封闭类的内部或外部定义它们。通俗点说就是嵌套类模板的作用范围是它所在类的里边,嵌套类的定义可以在外边定义。,如下是一段msdn上面的嵌套类的简单例子: 

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class X
     5 {
     6 
     7    template <class T>
     8    struct Y
     9    {
    10       T m_t;
    11       Y(T t): m_t(t) { }   
    12    };
    13 
    14    Y<int> yInt;
    15    Y<char> yChar;
    16 
    17 public:
    18    X(int i, char c) : yInt(i), yChar(c) { }
    19    void print()
    20    {
    21       cout << yInt.m_t << " " << yChar.m_t << endl;
    22    }
    23 };
    24 
    25 int main()
    26 {
    27    X x(1, 'a');
    28    x.print();
    29 }

       struct Y就是嵌套在class X中的一个类模板,在X类中模板Y实例化出了2个类型实例,分别是Y<int> 、Y<char>。此后所有的操作可以把Y<int>、Y<char>看做一个普通的类类型。

    6、在类模板中嵌套类模板

         与上一种情况类似,只是这次外层类是一个类模板,而非具体的类类型。这种情况下最值得注意的就是:

    When nested class templates are defined outside of their enclosing class, they must be prefaced by the template parameters for both the class template (if they are members of a class template) and template parameters for the member template.

    当嵌套类模板在其封闭类的外部定义时,它们必须以类模板(如果它们是类模板的成员)的模板参数和成员模板的模板参数开头。------MSDN手册

        翻译的也是比较拗口,但是读几遍的话也很好理解:当定义嵌套类时我们需要考虑的是,也就是第3中情况提到的需要注意的情况,”::“域作用符是作用于类上的,而非模板上的。所以如果在外部定义嵌套类时,首先我们不仅要在外层类模板后带上模板参数,使其形式上(还没有实例化)成为一个类,嵌套类也需要加上模板参数,使其形式上成为一个类。

     1 #include <iostream>
     2 using namespace std;
     3 
     4 template <class T>
     5 class X
     6 {
     7    template <class U> class Y
     8    {
     9       U* u;
    10    public:
    11       Y();
    12       U& Value();
    13       void print();
    14       ~Y();
    15    };
    16 
    17    Y<int> y;
    18 public:
    19    X(T t) { y.Value() = t; }
    20    void print() { y.print(); }
    21 };
    22 
    23 template <class T> 
    24 template <class U>
    25 X<T>::Y<U>::Y()
    26 {
    27    cout << "X<T>::Y<U>::Y()" << endl;
    28    u = new U();
    29 }
    30 
    31 template <class T> 
    32 template <class U>
    33 U& X<T>::Y<U>::Value()
    34 {
    35    return *u;
    36 }
    37 
    38 template <class T> 
    39 template <class U>
    40 void X<T>::Y<U>::print()
    41 {
    42    cout << this->Value() << endl;
    43 }
    44 
    45 template <class T> 
    46 template <class U>
    47 X<T>::Y<U>::~Y()
    48 {
    49    cout << "X<T>::Y<U>::~Y()" << endl;
    50    delete u;
    51 }
    52 
    53 int main()
    54 {
    55    X<int>* xi = new X<int>(10);
    56    X<char>* xc = new X<char>('c');
    57    xi->print();
    58    xc->print();
    59    delete xi;
    60    delete xc;
    61 }

        最后还值得注意的是,正如第四种情况那样,嵌套类与外层类的模板参数也有层次关系。通常写成两行:

    template <class T>
    template <class U>

        T被当成外层类的模板参数,U被当成嵌套类的模板参数。我看有的书上便于区分会写成缩进的形式:

    template <class T>
      template <class U>

       关于模板的知识多,暂时没精力写了,明天再接着写下去。有哪些地方不到位的,还请各位看官指正,谢谢。本人QQ:5435620.EMAIL:baixiangcpp@gmail.com。

  • 相关阅读:
    维特比算法 实现中文分词 python实现
    最大匹配算法进行分词 前向 后向 python实现
    动态规划 编辑距离问题(Edit Distance Problem)
    “RuntimeError: Trying to backward through the graph a second time, but the buffers have already been freed. Specify retain_graph=True when calling backward the first time”
    PCA主成分分析 原理讲解 python代码实现
    卷积的理解 python代码实现 pytorch 多输入多输出通道的理解
    AdaBoost python代码实现
    随机森林 python实现
    Apache配置转发
    JS 由前端保存到文件
  • 原文地址:https://www.cnblogs.com/ittinybird/p/4667510.html
Copyright © 2011-2022 走看看