类模板_类模板实例化 - CSDN
精华内容
参与话题
  • C++类模板 template 详细使用方法

    千次阅读 多人点赞 2017-11-27 21:48:26
    C++类模板 template 详细使用方法 类模板与函数模板的定义和使用类似。 有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类: class Compare_int { public : Compare(int a,...
    C++类模板 template <class T>详细使用方法
    类模板与函数模板的定义和使用类似。
    有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类:
    class Compare_int
    {
    public :
       Compare(int a,int b)
       {
          x=a;
          y=b;
       }
       int max( )
       {
          return (x>y)?x:y;
       }
       int min( )
       {
          return (x<y)?x:y;
       }
    private :
       int x,y;
    };
    其作用是对两个整数作比较,可以通过调用成员函数max和min得到两个整数中的大者和小者。
    如果想对两个浮点数(float型)作比较,需要另外声明一个类:
    class Compare_float
    {
    public :
       Compare(float a,float b)
       {
          x=a;y=b;
       }
       float max( )
       {
          return (x>y)?x:y;
       }
       float min( )
       {
          return (x<y)?x:y;
       }
    private :
       float x,y;
    }
    显然这基本上是重复性的工作,应该有办法减少重复的工作。
    C++在发展的后期增加了模板(template )的功能,提供了解决这类问题的途径。可以声明一个通用的类模板,它可以有一个或多个虚拟的类型参数,如对以上两个类可以综合写出以下的类模板:
    template <class numtype> //声明一个模板,虚拟类型名为numtype
    class Compare //类模板名为Compare
    {
    public :
       Compare(numtype a,numtype b)
       {
          x=a;y=b;
       }
       numtype max( )
       {
          return (x>y)?x:y;
       }
       numtype min( )
       {
          return (x<y)?x:y;
       }
    private :
       numtype x,y;
    };
    请将此类模板和前面第一个Compare_int类作一比较,可以看到有两处不同。

    1) 声明类模板时要增加一行
        template <class 类型参数名>
    template意思是“模板”,是声明类模板时必须写的关键字。在template后面的尖括号内的内容为模板的参数表列,关键字class表示其后面的是类型参数。在本例中numtype就是一个类型参数名。这个名宇是可以任意取的,只要是合法的标识符即可。这里取numtype只是表示“数据类型”的意思而已。此时,mimtype并不是一个已存在的实际类型名,它只是一个虚拟类型参数名。在以后将被一个实际的类型名取代。

    2) 原有的类型名int换成虚拟类型参数名numtype。
    在建立类对象时,如果将实际类型指定为int型,编译系统就会用int取代所有的numtype,如果指定为float型,就用float取代所有的numtype。这样就能实现“一类多用”。

    由于类模板包含类型参数,因此又称为参数化的类。如果说类是对象的抽象,对象是类的实例,则类模板是类的抽象,类是类模板的实例。利用类模板可以建立含各种数据类型的类。

    那么,在声明了一个类模板后,怎样使用它呢?怎样使它变成一个实际的类?

    先回顾一下用类来定义对象的方法:
        Compare_int cmp1(4,7); // Compare_int是已声明的类
    其作用是建立一个Compare_int类的对象,并将实参4和7分别赋给形参a和b,作为进 行比较的两个整数。

    用类模板定义对象的方法与此相似,但是不能直接写成
       Compare cmp(4,7); // Compare是类模板名
    Compare是类模板名,而不是一个具体的类,类模板体中的类型numtype并不是一个实际的类型,只是一个虚拟的类型,无法用它去定义对象。必须用实际类型名去取代虚拟的类型,具体的做法是:
        Compare <int> cmp(4,7);
    即在类模板名之后在尖括号内指定实际的类型名,在进行编译时,编译系统就用int取代类模板中的类型参数numtype,这样就把类模板具体化了,或者说实例化了。这时Compare<int>就相当于前面介绍的Compare_int类。
    [例] 声明一个类模板,利用它分别实现两个整数、浮点数和字符的比较,求出大数和小数。
    #include <iostream>
    using namespace std;
    template <class numtype>
    //定义类模板
    class Compare
    {
       public :
       Compare(numtype a,numtype b)
       {x=a;y=b;}
       numtype max( )
       {return (x>y)?x:y;}
       numtype min( )
       {return (x<y)?x:y;}
       private :
       numtype x,y;
    };
    int main( )
    {
       Compare<int > cmp1(3,7);  //定义对象cmp1,用于两个整数的比较
       cout<<cmp1.max( )<<" is the Maximum of two integer numbers."<<endl;
       cout<<cmp1.min( )<<" is the Minimum of two integer numbers."<<endl<<endl;
       Compare<float > cmp2(45.78,93.6);  //定义对象cmp2,用于两个浮点数的比较
       cout<<cmp2.max( )<<" is the Maximum of two float numbers."<<endl;
       cout<<cmp2.min( )<<" is the Minimum of two float numbers."<<endl<<endl;
       Compare<char> cmp3(′a′,′A′);  //定义对象cmp3,用于两个字符的比较
       cout<<cmp3.max( )<<" is the Maximum of two characters."<<endl;
       cout<<cmp3.min( )<<" is the Minimum of two characters."<<endl;
       return 0;
    }

    运行结果如下:
    7 is the Maximum of two integers.
    3 is the Minimum of two integers.

    93.6 is the Maximum of two float numbers.
    45.78 is the Minimum of two float numbers.

    a is the Maximum of two characters.
    A is the Minimum of two characters.

    还有一个问题要说明: 上面列出的类模板中的成员函数是在类模板内定义的。如果改为在类模板外定义,不能用一般定义类成员函数的形式:
        numtype Compare::max( ) {…} //不能这样定义类模板中的成员函数
    而应当写成类模板的形式:
        template <class numtype>
        numtype Compare<numtype>::max( )
        {
            return (x>y)?x:y;
        }
    上面第一行表示是类模板,第二行左端的numtype是虚拟类型名,后面的Compare <numtype>是一个整体,是带参的类。表示所定义的max函数是在类Compare <numtype>的作用域内的。在定义对象时,用户当然要指定实际的类型(如int),进行编译时就会将类模板中的虚拟类型名numtype全部用实际的类型代替。这样Compare <numtype >就相当于一个实际的类。大家可以将例9.14改写为在类模板外定义各成员 函数。

    归纳以上的介绍,可以这样声明和使用类模板:
    1) 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。

    2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。

    3) 在类声明前面加入一行,格式为:
        template <class 虚拟类型参数>
    如:
        template <class numtype> //注意本行末尾无分号
        class Compare
        {…}; //类体

    4) 用类模板定义对象时用以下形式:
        类模板名<实际类型名> 对象名;
        类模板名<实际类型名> 对象名(实参表列);
    如:
        Compare<int> cmp;
        Compare<int> cmp(3,7);

    5) 如果在类模板外定义成员函数,应写成类模板形式:
       template <class 虚拟类型参数>
       函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}

    关于类模板的几点说明:
    1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如:
        template <class T1,class T2>
        class someclass
        {…};
    在定义对象时分别代入实际的类型名,如:
        someclass<int,double> obj;

    2) 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。

    3) 模板可以有层次,一个类模板可以作为基类,派生出派生模板类。有关这方面的知识实际应用较少,本教程暂不作介绍,感兴趣的同学可以自行学习。

    展开全文
  • C++类模板和模板类

    万次阅读 2018-11-29 14:32:35
    C++通过类模板来实现泛型支持。 1 基础的类模板 类模板,可以定义相同的操作,拥有不同数据类型的成员属性。 通常使用template来声明。告诉编译器,碰到T不要报错,表示一种泛型. 如下,声明一个普通的类模板: ...

    C++
    中有一个重要特性,那就是模板类型。类似于Objective-C中的泛型。C++通过类模板来实现泛型支持。

    1 基础的类模板

    类模板,可以定义相同的操作,拥有不同数据类型的成员属性。

    通常使用template来声明。告诉编译器,碰到T不要报错,表示一种泛型.

    如下,声明一个普通的类模板:

    template <typename T>
    class Complex{
        
    public:
        //构造函数
        Complex(T a, T b)
        {
            this->a = a;
            this->b = b;
        }
        
        //运算符重载
        Complex<T> operator+(Complex &c)
        {
            Complex<T> tmp(this->a+c.a, this->b+c.b);
            return tmp;
        }
            
    private:
        T a;
        T b;
    }
    
    int main()
    {
        //对象的定义,必须声明模板类型,因为要分配内容
        Complex<int> a(10,20);  
        Complex<int> b(20,30);
        Complex<int> c = a + b;
        
        return 0;
    }
    
    

    2 模板类的继承

    在模板类的继承中,需要注意以下几点:

    • 如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化
    • 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
    • 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
    template <typename T>
    class Parent{
    public:
        Parent(T p)
        {
            this->p = p;
        }
        
    private:
        T p;
    };
    
    //如果子类不是模板类,需要指明父类的具体类型
    class ChildOne:public Parent<int>{
        
    public:
        ChildOne(int a,int b):Parent(b)
        {
            this->cone = a;
        }
        
    private:
        int cone;
    };
    
    
    //如果子类是模板类,可以用子类的泛型来表示父类
    template <typename T>
    class ChildTwo:public Parent<T>{
        
    public:
        ChildTwo(T a, T b):Parent<T>(b)
        {
            this->ctwo = a;
        }
        
    private:
        T ctwo;
    };
    
    

    3 内部声明定义普通模板函数和友元模板函数

    普通模板函数和友元模板函数,声明和定义都写在类的内部,也不会有什么报错。

    template <typename T>
    class Complex {
        
        //友元函数实现运算符重载
        friend ostream& operator<<(ostream &out, Complex &c)
        {
            out<<c.a << " + " << c.b << "i";
            return out;
        }
        
    public:
        Complex(T a, T b)
        {
            this->a = a;
            this->b = b;
        }
        
        //运算符重载+
        Complex operator+(Complex &c)
        {
            Complex temp(this->a + c.a, this->b + c.b);
            return temp;
        }
        
        //普通加法函数
        Complex myAdd(Complex &c1, Complex &c2)
        {
            Complex temp(c1.a + c2.a, c1.b + c2.b);
            return temp;
        }
        
    private:
        T a;
        T b;
    };
    
    int main()
    {
        Complex<int> c1(1,2);
        Complex<int> c2(3,4);
        
        Complex<int> c = c1 + c2;
        
        cout<<c<<endl;
        
        return 0;
    }
    
    

    4 内部声明友元模板函数+外部定义友元模板函数

    如果普通的模板函数声明在内的内部,定义在类的外部,不管是否处于同一个文件,就跟普通的函数一样,不会出现任何错误提示。但是如果是友元函数就会出现报错,是因为有二次编译这个机制存在。

    4.1 模板类和模板函数的机制

    在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。我们称之为二次编译。同样,因为这个机制,会经常报错找不到类的函数的实现。在模板类的友元函数外部定义时,也会出现这个错误。解决方法是 “ 类的前置声明和函数的前置声明 ”。

    • 按照普通模板函数的样式处理友元函数
    #include <iostream>
    using namespace std;
    
    
    template <typename T>
    class Complex {
        
        //友元函数实现运算符重载
        friend ostream& operator<<(ostream &out, Complex<T> &c);
        
    public:
        Complex(T a, T b);
        
        //运算符重载+
        Complex<T> operator+(Complex<T> &c);
        
        //普通加法函数
        Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
        
    private:
        T a;
        T b;
    };
    
    //友元函数的实现
    template <typename T>
    ostream& operator<<(ostream &out, Complex<T> &c)
    {
        out<<c.a << " + " << c.b << "i";
        return out;
    }
    
    
    //函数的实现
    template <typename T>
    Complex<T>::Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
    
    template <typename T>
    Complex<T> Complex<T>::operator+(Complex<T> &c)
    {
        Complex temp(this->a + c.a, this->b + c.b);
        return temp;
    }
    
    template <typename T>
    Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
    {
        Complex temp(c1.a + c2.a, c1.b + c2.b);
        return temp;
    }
    
    
    int main()
    {
        Complex<int> c1(1,2);
        Complex<int> c2(3,4);
        
        Complex<int> c = c1 + c2;
        
        cout<<c<<endl;
        
        return 0;
    }
    
    
    • 友元函数的定义写在类的外部–错误信息
    Undefined symbols for architecture x86_64:
      "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Complex<int>&)", referenced from:
          _main in demo1.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    上面的错误信息,就是典型的二次编译的错误信息,找不到友元函数的函数实现。所以,如果友元模板函数的定义写在函数的外部,需要进行类和函数的前置声明,来让编译器找到函数的实现

    4.2 前置声明解决二次编译问题
    • 类的前置声明
    • 友元模板函数的前置声明
    • 友元模板函数声明需要增加泛型支持

    5 声明和定义分别在不同的文件(模板函数、模板友元)

    类的声明和实现,分别在不同的文件下,需要增加一个hpp文件支持。或者尽量将模板函数与模板友元放在一个文件下。

    • 类的声明与函数的声明写在.h文件
    • 类的实现及函数的实现写在.cpp文件
    • 将.cpp文件改成.hpp文件
    • 在主函数中调用.hpp文件,而不是引用.h文件

    如果碰到.h和.hpp文件都存在的情况下,引用.hpp文件。

    demo2.h文件
    存放类的声明和函数的声明

    #include <iostream>
    using namespace std;
    
    //类的前置声明
    template <typename T>
    class Complex;
    
    //友元函数的声明
    template <typename T>
    ostream& operator<<(ostream &out, Complex<T> &c);
    
    template <typename T>
    class Complex {
        
        //友元函数实现运算符重载
        friend ostream& operator<< <T> (ostream &out, Complex<T> &c);
        
    public:
        Complex(T a, T b);
        
        //运算符重载+
        Complex<T> operator+(Complex<T> &c);
        
        //普通加法函数
        Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
        
    private:
        T a;
        T b;
    };
    
    

    demo2.hpp文件
    包括模板函数的实现

    #include "demo2.h"
    
    //友元函数的实现
    template <typename T>
    ostream& operator<<(ostream &out, Complex<T> &c)
    {
        out<<c.a << " + " << c.b << "i";
        return out;
    }
    
    
    //函数的实现
    template <typename T>
    Complex<T>::Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
    
    template <typename T>
    Complex<T> Complex<T>::operator+(Complex<T> &c)
    {
        Complex temp(this->a + c.a, this->b + c.b);
        return temp;
    }
    
    template <typename T>
    Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
    {
        Complex temp(c1.a + c2.a, c1.b + c2.b);
        return temp;
    }
    

    main.cpp文件
    需要调用hpp文件

    #include <iostream>
    using namespace std;
    #include "demo2.hpp"
    
    
    int main()
    {
        Complex<int> c1(1,2);
        Complex<int> c2(3,4);
        
        Complex<int> c = c1 + c2;
        
        cout<<c<<endl;
        
        return 0;
    }
    
    

    更多参考

    展开全文
  • C++之——类模板与函数模板用法

    千次阅读 2018-09-28 10:51:28
    通常有两种形式:函数模板和类模板; 函数模板针对仅参数类型不同的函数; 类模板针对仅数据成员和成员函数类型不同的类。 使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int ...

     

    模板是一种对类型进行参数化的工具;
    通常有两种形式:函数模板和类模板;
    函数模板针对仅参数类型不同的函数;
    类模板针对仅数据成员和成员函数类型不同的类。
    使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。下面分别介绍。
    注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
    一、函数模板通式
    1、函数模板的格式:
    template <class 形参名,class 形参名,......>
    返回类型 函数名(参数列表)
    {
    函数体
    }
    其中template和class是关键字,class可以用typename 关键字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为
    template <class T> void swap(T& a, T& b){},当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中a和b是int 型,这时模板函数swap中的形参T就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。
    2、注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
    二、类模板通式
    1、类模板的格式为:
    template<class  形参名,class 形参名,…>   
    class 类名
    { ... };
    类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如
    template<class T> class A{public: T a; T b; T hy(T c, T &d);};
    在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
    2、类模板对象的创建:比如一个模板类A,则使用类模板创建对象的方法为A<int> m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;类型之间用逗号隔开。
    3、对于类模板,模板形参的类型必须在类名后的尖括号中明确指定。比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: 'a' uses undefined class 'A<int>'),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A<int> m。
    4、在类模板外部定义成员函数的方法为:
    template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},
    比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
    template<class T1,class T2> void A<T1,T2>::h(){}。
    注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
    5、再次提醒注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。函数模板和类模板;
    函数模板针对仅参数类型不同的函数;
    类模板针对仅数据成员和成员函数类型不同的类。
    使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。下面分别介绍。
    注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
    一、函数模板通式
    1、函数模板的格式:
    template <class 形参名,class 形参名,......>
    返回类型 函数名(参数列表)
    {
    函数体
    }
    其中template和class是关键字,class可以用typename 关键字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为
    template <class T> void swap(T& a, T& b){},当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中a和b是int 型,这时模板函数swap中的形参T就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中c和d是double类型时,模板函数会被替换为swap(double &a, double &b),这样就实现了函数的实现与类型无关的代码。
    2、注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)。
    二、类模板通式
    1、类模板的格式为:
    template<class  形参名,class 形参名,…>   
    class 类名
    { ... };
    类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如
    template<class T> class A{public: T a; T b; T hy(T c, T &d);};
    在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
    2、类模板对象的创建:比如一个模板类A,则使用类模板创建对象的方法为A<int> m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int, double> m;类型之间用逗号隔开。
    3、对于类模板,模板形参的类型必须在类名后的尖括号中明确指定。比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: 'a' uses undefined class 'A<int>'),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A<int> m。
    4、在类模板外部定义成员函数的方法为:
    template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},
    比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
    template<class T1,class T2> void A<T1,T2>::h(){}。
    注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。
    5、再次提醒注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

    用类模板实现对任意类型数据的存取实例:

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    struct Student {
    	int id;
    	float gpa;//平均分
    };
    template <class T>
    class Store {//类模板,实现对任意类型数据进行存取
    private:
    	T item;//item用于存放任意类型的数据
    	bool haveValue;//haveValue标记item是否已被存入内容
    public://成员函数用来操作数据成员
    	Store();//构造函数
    	T &getElem();
    	void putElem(const T &x);//存入数据函数
    };
    
    template <class T>//以下是类成员函数模板
    Store<T>::Store():haveValue(false){}//构造函数用来初始化私有数据成员
    template <class T>
    T &Store<T>::getElem() {//注意写法
    	//如试图提取未初始化的数据,则终止程序
    	if (!haveValue) {
    		cout << "No item present!" << endl;
    		exit(1);//使程序完全退出,返回到操作系统
    	}
    	return item;//返回item中存放的数据
    }
    template <class T>
    void Store<T>::putElem(const T &x) {
    	//将haveValue的值置为TRUE,表示item中已存入数组
    	haveValue = true;
    	item = x;//将x存入item
    }
    
    
    int main()
    {
    	Store<int> s1, s2;
    	s1.putElem(3);
    	s2.putElem(-4);
    	cout << s1.getElem() << " " << s2.getElem() << endl;
    
    	Student g = { 1000,23 };
    	Store<Student> s3;
    	s3.putElem(g);
    	cout << "The student id is " << s3.getElem().id << endl;
    	Store<double> d;
    	cout << "Retrieving object D...";
    	cout << d.getElem() << endl;
    	//d未初始化。执行函数D.getelem()导致程序终止
        return 0;
    }
    #include <algorithm>
    #include <iterator>
    #include <vector>
    #include <iostream>
    using namespace std;
    
    template <class T,class InputIterator,class OutputIterator>
    void mySort(InputIterator first, InputIterator last, OutputIterator result) {
    	vector<T> s;
    	for (; first != last; ++first)
    		s.push_back(*first);
    	sort(s.begin(), s.end());
    	copy(s.begin(), s.end(), result);
    }
    int main() {
    	double a[5] = { 1.2,2.4,0.8,3.3,6.6 };
    	//将s数组内容排序后输出
    	mySort<double>(a, a + 5, ostream_iterator<double>(cout, " "));
    	cout << endl;
    	//从标准输入设备输入若干整数,将排序结果输出到输出设备
    	mySort<int>(istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator<int>(cout, " "));
    	cout << endl;
    	return 0;
    
    }
    

    函数传参:(借鉴原文:https://blog.csdn.net/hudfang/article/details/52934655)

     

    (1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

    (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

    (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

    --------------------------------------------------

    如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;
    例1
    int a ;
    const int &ra=a;
    ra=1; //错误
    a=1; //正确
    例2
    string foo( );
    void bar(string & s);
    那么下面的表达式将是非法的:
    bar(foo( ));
    bar(“hello world”);
    原因在于foo( )和”hello world”串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
    引用型参数应该在能被定义为const的情况下,尽量定义为const 。

    返回值为引用类型的函数某些情况下可作为表达式的左值,而非引用的则一般不可以。
    函数返回值若为引用类型,当返回的是函数的引用形参时,则是对函数外的变量的引用,函数可以作为表达式的左值(被赋予新值)。 
    函数返回值若为引用类型,当返回的是函数的引用形参时,则是对函数外的变量的引用,函数可以作为表达式的左值(被赋予新值)。 
    
    而当函数返回的是非引用类型时,返回的值是函数内隐式生成的临时变量,当函数结束析构时释放,函数作为左值被赋予新值没有意义或产生错误。(例外情况,当返回的是函数中用new等动态内存分配函数建立的指针时,可作为左值。)
    注:“引用类型”并非是一种新的类型,也没有这种类型,只是为了口头上的方便说的,是对某某类型变量的引用的个人说法。而当函数返回的是非引用类型时,返回的值是函数内隐式生成的临时变量,当函数结束析构时释放,函数作为左值被赋予新值没有意义或产生错误。(例外情况,当返回的是函数中用new等动态内存分配函数建立的指针时,可作为左值。)
    注:“引用类型”并非是一种新的类型,也没有这种类型,只是为了口头上的方便说的,是对某某类型变量的引用的个人说法。

     

    展开全文
  • 类模板和模板类

    万次阅读 多人点赞 2019-07-04 21:10:42
    类模板和模板类 所谓类模板,实际上是建立一个通用类,其数据成员、成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表。使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从而实现了...

    类模板和模板类

    所谓类模板,实际上是建立一个通用类,其数据成员、成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表。使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从而实现了不同类的功能。

    定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,后面是尖括号括起来的模板参数,然后是类名,其格式如下:

    template <typename 类型参数>
    class 类名{
           类成员声明 
    };
    
    或者
    
    template <class 类型参数>
    class 类名{
           类成员声明 
    };
    
    1. template:是一个声明模板的关键字,它表明声明一个模板

    2. 类型参数:通常用C++标识符表示,如T、Type等,实际上是一个虚拟的类型名,现在未指定它是哪一种具体的类型,但使用类模板时,必须将类型参数实例化。

    3. typename和class的作用相同,都是表示其后面的参数是一个虚拟的类名(即类型参数).

    在类声明中,欲采用通用数据类型的数据成员、成员函数的参数或返回类型前面需要加上类型参数。

    如建立一个用来实现求两个数最大值的类模板

    template<typename T>    //模板声明,其中T为类型参数
        class Compare{
          public:
           Compare(T i,T j)
           {
            x = i;
            y = j;
           }
           T max()
           {
            return (x>y)?x:y;
           } 
          private:
           T x,y; 
        };
    

    用类模板定义对象时,采用以下形式:

    类模板名<实际类型名>对象名[(实参表列)];
    

    因此,使用上面求最大值的类型模板的主函数可写成:

     int main()
         {
          Compare<int>com1(3,7);
          Compare<double>com2(12.34,56.78);
          Compare<char>com3('a','x');
          cout<<"其中的最大值是:"<<com1.max()<<endl;
          cout<<"其中的最大值是:"<<com2.max()<<endl;
          cout<<"其中的最大值是:"<<com3.max()<<endl;
          return  0;
         }    
    

    例6.6 类模板compare的使用举例

    #include<iostream.h>
    template<typename T>    //模板声明,其中T为类型参数
    class Compare{
      public:
        Compare(T i,T j)
        {
          x = i;
          y = j;
        }
        T max()
         {
          return (x>y)?x:y;
         } 
     private:
        T x,y; 
    };
    int main()
    {
    Compare<int>com1(3,7);                       //用类模板定义对象com1,此时T被int替代 
    Compare<double>com2(12.34,56.78);            //用类模板定义对象com2,此时T被double替代 
    Compare<char>com3('a','x');                  //用类模板定义对象com3,此时T被char替代 
    cout<<"其中的最大值是:"<<com1.max()<<endl;   
    cout<<"其中的最大值是:"<<com2.max()<<endl;
    cout<<"其中的最大值是:"<<com3.max()<<endl;
    return  0;
    }
    

    程序运行结果是:

    其中的最大值是:7
    其中的最大值是:56.78
    其中的最大值是:x        
    

    在以上例子中,成员函数(其中含有类型参数)是定义类体内的。但是,类模板中的成员函数,也可以在类模板外定义。此时,若成员函数中有参数类型存在,则C++有一些特殊的规定:

    (1)需要在成员函数定义之前进行模板声明;
    (2)在成员函数名前缀上"类名<类型参数>::";
    

    在类模板外定义成员函数的一般形式如下:

    temlate<typename 类型参数>
      函数类型 类名<类型参数>::成员函数名(形参表)
      {
        函数体; 
       }
       
       如上题中成员函数max在类模板外定义时,应该写成:
       template<typename T>
       T Compare<T>::max()
       {
        return (x>y)?x:y;
       } 
    

    //例6.7 在类模板外定义成员函数函数举例。

    #include<iostream.h>
    template<typename T>    //模板声明,其中T为类型参数
    class Compare{
      public:
        Compare(T i,T j)
        {
          x = i;
          y = j;
        }
        T max(); 
     private:
        T x,y; 
    };
    template<class T>
    T Compare<T>::max()
    {
     return (x>y)?x:y;
    }
    int main()
    {
    Compare<int>com1(3,7);                       //用类模板定义对象com1,此时T被int替代 
    Compare<double>com2(12.34,56.78);            //用类模板定义对象com2,此时T被double替代 
    Compare<char>com3('a','x');                  //用类模板定义对象com3,此时T被char替代 
    cout<<"其中的最大值是:"<<com1.max()<<endl;   
    cout<<"其中的最大值是:"<<com2.max()<<endl;
    cout<<"其中的最大值是:"<<com3.max()<<endl;
    return  0;
    } 
    

    /*
    程序运行结果是:

        其中的最大值是:7
        其中的最大值是:56.78
        其中的最大值是:x
    

    此例中,类模板Compare经实例化后生成了3个类型分别为int、double、char的模板类,这3个模板类
    经实例化后又生成了3个对象com1、com2、com3。类模板代表了一类类,模板类表示某一具体的类。关系如下:

                                             类模板
                                            Compare<T>
        实例化成模板类:Compare<int>       Compare<double>     Compare<char>
        实例化模板类对象:com1                 com2                com3
    

    例6.8 类模板Stack的使用举例。

    #include<iostream.h>
    const int size=10;
    template<class T>                     //模板声明,其中T为类型参数 
    class Stack{                          //类模板为Stack 
     public:
      void init()
      {
       tos=0;
      }
      void push(T ob);                    //声明成员函数push的原型,函数参数类型为T类型
      T pop();                            //声明成员函数pop的原型,其返回值类型为T类型
     private:
      T stack[size];                      //数组类型为T,即是自可取任意类型 
      int tos; 
    };
    template<class T>                     //模板声明 
    void Stack<T>::push(T ob)             //在类模板体外定义成员函数push 
    {
      if(tos==size)
       {
        cout<<"Stack is full"<<endl;
        return;
       }
      stack[tos]=ob;
      tos++; 
    }
    template<typename T>                  //模板声明 
    T Stack<T>::pop()                               //在类模板体外定义成员函数push
    {
      if(tos==0)
       {
        cout<<"Stack is empty"<<endl;
        return 0;
       }
      tos--; 
      return stack[tos];  
    }
    int main()
    {
     //定义字符堆栈 
     Stack<char> s1;                        //用类模板定义对象s,此时T被char取代
     s1.init();
     s1.push('a');
     s1.push('b');
     s1.push('c'); 
     for(int i=0;i<3;i++){cout<<"pop s1:"<<s1.pop()<<endl;}
     
     //定义整型堆栈 
     Stack<int> s2;                        //用类模板定义对象s,此时T被int取代
     s2.init();
     s2.push(1);
     s2.push(3);
     s2.push(5); 
     for(int i=0;i<3;i++){cout<<"pop s2:"<<s2.pop()<<endl;} 
     
     return 0; 
    }
    

    /*
    程序运行结果是:

    pop s1:c
    pop s1:b
    pop s1:a
    pop s2:5
    pop s2:3
    pop s2:1 
    

    说明:

    1. 在每一个类模板定义之前,都需要在前面加上模板声明,如

      template

      template
      并且,类模板在使用时,必须在模板类名字后面缀上<类型参数> ,如
      Stack

    2. 如同模板函数一样,模板类也可以有多个类型参数。

    例6.9 有两个类型参数的类模板举例

    #include<iostream.h>
    template<class QQ,class T>                    //声明模板,具有T1,T2两个类型参数 
    class Myclass{                                 //定义模板类Myclass 
      public:
       Myclass(QQ a,T b);
       void show();
      private:
       QQ x;
       T y;
    };
    template<typename QQ,typename T>
    Myclass<QQ,T>::Myclass(QQ a,T b)
    {
     x = a;
     y = b;
    }
    template<class QQ,class T>
    void Myclass<QQ,T>::show()
    {
     cout<<"x="<<x<<","<<"y="<<y<<endl;
    }
    int main()
    {
     Myclass <int,double>m1(12,0.15);               //用类模板定义对象m1,此时T1,T2分别被int、double取代 
     Myclass <int,char*>m2(12,"This a test.");      //用类模板定义对象m2,此时T1,T2分别被int,char*取代
    
     m1.show();
     m2.show();
     
     return 0; 
    }
    /*
    程序运行结果是:
    x=12,y=0.15
    x=12,y=This a test. 
    */
    

    程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!
    更多C++相关知识体系,请移步C++知识目录

    展开全文
  • C++ 类模板与模板类详解

    万次阅读 多人点赞 2018-03-24 17:22:58
    转自:https://www.cnblogs.com/cxq0017/p/6076856.html在C++的Template... 事实上class用于定义,在模板引入c++后,最初定义模板的方法为:template&lt;class T&gt;,这里class关键字表明T是一个类型,后...
  • 类模板

    2018-05-29 11:36:22
    #include&lt;iostream&gt; using namespace std; template &lt;class numtype&gt;//声明 class Compare {public: Compare(numtype a, numtype b); numtype max();... n...
  • 模板类

    2019-03-13 10:06:57
    c++中模板的实现(模板类模板函数) 模板  当我们实现一个交换函数时,我们可以写成如下。 void Swap(int&amp; x, int&amp; y) { int tmp = x; x = y; y = tmp; }  这里只能交换两个整数,当...
  • 类模板和模板类的区别

    千次阅读 2008-11-19 01:44:00
    "一般来讲,类模版是结构相似但不同的类的抽象,是描述性的;..." 类模板只是一个抽象的描述,在应用时在内存中是不占空间的, 而模板类是一个具体的东西! 类模板强调的是模板 template class A {};
  • IntelliJ IDEA设置注释和方法注释模板

    万次阅读 多人点赞 2018-06-03 15:53:21
    IntelliJ IDEA设置注释和方法注释模板1、设置注释模板这样在定义时,都要多输入的描述。不想的话,可以删去 ${description}2、方法注释模板先新建模板组,名字自己起。然后选中自己的模板组,在模板组下...
  • 超详细设置Idea注释模板和方法注释模板

    万次阅读 多人点赞 2017-12-11 17:36:39
    设置注释模板1.选择File–>Settings–>Editor–>File and Code Templates–>Includes–>File Header. 2.在右边空白处,编写自己的模板即可,注意Scheme是模板的生效范围,可选变量在description有介绍,附图中...
  • idea设置注释和方法注释模板

    万次阅读 多人点赞 2020-10-16 11:48:01
    1、设置注释模板 这样在定义时,都要多输入的描述。不想的话,可以删去 ${description} 2、方法注释模板 先新建模板组,名字自己起。 然后选中自己的模板组,在模板组下新建模板 如下图给...
  • 缺少模板参数列表问题

    万次阅读 2016-06-04 12:02:55
    //-----------------Stack.c--------------------- template class Stack { public: Stack(); ~Stack(); }; //-----------------Stack.cpp--------------------- #include "Stack.h" template Stac
  • C++ 类模板和模板类

    万次阅读 2015-12-28 20:19:23
    类模板的意义和函数模板的意义是一样的。 类模板的定义 template //声明一个模板,虚拟类型名为T。注意:这里没有分号。 class Compare //类模板名为Compare { public : Compare(T a,T b) { x=a;y=b; } T max( ...
  • 类模板与模板类

    万次阅读 热门讨论 2009-02-15 18:44:00
    1.类模板与模板类的概念⑴ 什么是类模板 一个类模板(也称为类属类或类生成类)允许用户为类定义一种模式,使得类中的某些数据成员、默写成员函数的参数、某些成员函数的返回值,能够取任意类型(包括系统预定义的和...
  • IDEA创建类模板和方法模板(超详细)

    万次阅读 多人点赞 2018-08-16 23:54:55
    创建类模板 按照顺序打开File–&gt;settings–&gt;Editor–&gt;File and Code Templates–&gt;Includes 输入类注释模板 /** * @Classname ${NAME} * @Description TODO * @Date $...
  • java的模板类

    万次阅读 2013-01-09 16:15:46
    java的模板类可以理解为含有Object类型的。   1. java的模板类模板参数只能是参数类型,成员变量类型等,模板名是确定的。 2. 运行期,模板参数会被当作Object来处理,已经验证 3. 使用模板类的类型安全,...
  • Intellij Idea设置模板类和方法备注模板 在IntellijIdea中我并没有找到直接对某个方法进行注释的模板,因此采用了一个折中的方式:使用自定义的快捷键。
  • C++:模板总结

    万次阅读 多人点赞 2019-03-16 12:34:26
    写在前面 &nbsp;&nbsp;&nbsp;&nbsp;&...模板(Template)指C++程序设计设计...模板是C++支持参数化多态的工具,使用模板可以使用户为或者函数声明一种一般模式,使得中的某些数据成员或者成...
  • Eclipse设置和方法的注释模板

    万次阅读 2018-03-31 20:06:42
    一.打开设置模板的窗口:Window-&gt;Preference-&...1.选择Types,这个是给设置自动注释模板,我设置的模板如下: /** @version:(版本,具体版本信息自己来定) * @Description: (对...
  • IDEA和方法注释模板设置(非常详细)

    万次阅读 多人点赞 2018-10-22 15:58:03
    IDEA自带的注释模板不是太好用,我本人到网上搜集了很多资料系统的整理了一下制作了一份比较完整的模板来分享给大家,我不是专业玩博客的,写这篇文章只是为了让大家省事。 这里设置的注释模板采用Eclipse的格式,...
1 2 3 4 5 ... 20
收藏数 660,981
精华内容 264,392
关键字:

类模板