C++函数模板与类模板相同与不同介绍

目录
  • 1.模板
    • 1.1何为模板
    • 1.2C++的模板的形式有两种
    • 1.3如何定义一个函数模板
    • 1.4语法形式
    • 1.5模板的编译机制
  • 2.函数模板
    • 2.1调用方式
    • 2.2函数模板的特化与调用优先级
  • 3.可变参函数模板
    • 3.1概念
    • 3.2代码实现(实现一个c中的printf的函数)
  • 4.类模板
    • 4.1类模板的定义形式
    • 4.2代码实例
  • 5.类模板中的特殊属性的初始化方式及继承与多态
    • 5.1代码实例
    • 5.2使用类模板去实现一个数据结构
    • 5.3类模板的特化
    • 5.4C++中类模板中的内嵌类

1.模板

1.1何为模板

即模子,生成器。

1.2C++的模板的形式有两种

函数模板与类模板。

1.3如何定义一个函数模板

就像定义函数一样,定义一个函数模板,把函数中类型抽象出来,

同时告诉编译器下面的函数是一个函数模子或函数生成器。

1.4语法形式

template + <class T1, class T2 = double, class T3 = int ....>
//模板头中的类型参数列表也可以有默认值。
T add(T a, T b)
{
    return a + b;
}

1.5模板的编译机制

1.编译器并不是把模板处理成能够处理任何类型的函数,而是一个函数或类的生成器。

2.函数模板通过具体类型产生不同的函数(产生了模板函数)

3.编译器会对函数模板进行两次编译,第一次在声明的地方对模板本身进行编译,在调用的地方对参数替换后的代码进行编译使用函数模板与真正的函数,谁的调用效率高呢?当然是真正的函数,因为函数模板还需要编译器进行翻译一遍才能调用。所以现开发中并不一定要把所有函数你都要定义成函数模板,所以类型替换之后的函数模板就成了一个函数实例了,这样才能调用。

代码实例:

#include <iostream>
using namespace std;
template <class T>
T my_add(T a, T b)
{
    return a + b;
}
int main()
{
    my_add<int>(10,20);
    my_add<float>(3.14f,5.21f);
    my_add<double>(5.21,3.14);
    return 0;
}

2.函数模板

2.1调用方式

显式调用:函数名后使用<>尖括号指定具体参数调用。

using namespace std;
template <class T>
T my_add(T a, T b)
{
    return a + b;
}
int main()
{
    my_add<int>(10,20);
    my_add<float>(3.14f,5.21f);
    my_add<double>(5.21,3.14);
    return 0;
}

隐式调用:由编译器自动根据参数推导参数类型再调用。

#include <iostream>
using namespace std;
template <class T>
T my_add(T a, T b)
{
    return a + b;
}
int main()
{
    my_add(10,20);
    my_add(3.14f,5.21f);
    my_add(5.21,3.14);
    return 0;
}

2.2函数模板的特化与调用优先级

当只有基础模板和特化模板时。

#include <iostream>
using namespace std;
template <class T>
T my_add(T a,T b)
{
    cout<<"这是一个基础模板"<<endl;
    return a+b;
}
template <class T>
T my_add(int a,int b)
{
    cout<<"这是一个特化模板"<<endl;
    return a+b;
}
int main()
{
    cout<<my_add(10,20)<<endl;
    return 0;
}

结果图:

当只有基础模板和特化模板时,隐式调用基础模板。

当有基础模板和特化模板,还有实例时候。

#include <iostream>
using namespace std;
template <class T>
T my_add(T a,T b)
{
    cout<<"这是一个基础模板"<<endl;
    return a+b;
}
template <class T>
T my_add(int a,int b)
{
    cout<<"这是一个特化模板"<<endl;
    return a+b;
}
int my_add(int a,int b)
{
    cout<<"这是一个实例"<<endl;
    return a+b;
}
int main()
{
    cout<<my_add(10,20)<<endl;
    return 0;
}

结果图:

当有基础模板和特化模板,还有实例时候,隐式调用用实例。

当有基础模板和特化模板,还有实例时候,显示调用

#include <iostream>
using namespace std;
template <class T>
T my_add(T a,T b)
{
    cout<<"这是一个基础模板"<<endl;
    return a+b;
}
template <class T>
T my_add(int a,int b)
{
    cout<<"这是一个特化模板"<<endl;
    return a+b;
}
int my_add(int a,int b)
{
    cout<<"这是一个实例"<<endl;
    return a+b;
}
int main()
{
   cout<<my_add<int>(10,20)<<endl;
    return 0;
}

结果图:

当有基础模板和特化模板,还有实例时候,显示调用用特化模板。

总结:

当有函数实例时:隐式调用将直接调用函数实例。

如果没有函数实例时,隐式调用将直接调用函数模板的基础模板。

如果使用显示调用,当优先调用特化的与类型匹配的函数模板。

3.可变参函数模板

3.1概念

所谓的可变参模板是指类型参数为一一个可变是类型,这个类型使用class...来修饰。

3.2代码实现(实现一个c中的printf的函数)

#include <iostream>
using namespace std;
void printf()
{
}
template <class Firstarg,class... Arg>
void printf(Firstarg firstarg, Arg... arg)
{
    cout<<firstarg;
    printf(arg...);
}
int main()
{
    printf("lisi","cc");
    return 0;
}

结果图:

4.类模板

4.1类模板的定义形式

注意:在使用类模板时,不存在编译推导类型,必须手动指定具体类型。

template <class T1, class T2, class T3 ...>
//class修饰符也可使用typename来修饰。
class + 类名
{
    //类模板的模板体。
    private:
        //类模板中的属性。
    public:
        //类中的方法
    protected:
};

4.2代码实例

#include <iostream>
using namespace std;
template <class T1,class T2>
class A
{
    T1 name;
    T2 age;
public:
    A(T1 name,T2 age)
    {
        this->age=age;
        this->name=name;
    }
    void show_info()
    {
        cout<<"name="<<name<<",age="<<age<<endl;
    }
};
int main()
{
//    A<string,int> a("lisi",20);
//    a.show_info();
    A<string,int>* a=new A<string,int>("lisi",20);
    a->show_info();
    return 0;
}

结果图:

5.类模板中的特殊属性的初始化方式及继承与多态

5.1代码实例

#include <iostream>
using namespace std;
template  <class T1,class T2>
class A
{
    T1 name;
    T2 age;
public:
    A(T1 name,T2 age)
    {
        this->name=name;
        this->age=age;
    }
    virtual void show_info()
    {
        cout<<"name="<<this->name<<",age"<<age<<endl;
    }
    void set_name(T1 name)
    {
        this->name=name;
    }
    T1 get_name()
    {
        return  this->name;
    }
    void set_age(T2 age)
    {
        this->age=age;
    }
    T2 get_age()
    {
        return this->age;
    }
};
template <class T1,class T2,class T3>
class B:public A<T1,T2>
{
    const int id;
    static int count;
public:
    B(T1 name,T2 age,T3 _id):id(_id),A<T1,T2>(name,age)
    {
    }
    void show_info()
    {
        cout<<"id="<<this->id<<",name="<<this->get_name()<<",age"<<this->get_age()<<endl;
    }
};
int main()
{
    //1.栈上
    B<string,int,int> b("lisi",20,1001);
    b.show_info();
    //2.堆上
    B<string,int,int>* b1=new B<string,int,int>("zhangsan",29,1002);
    b1->show_info();
    //3.实现多态
    A<string,int>* a=new B<string,int,int>("wangwu",50,1003);
    a->show_info();
    return 0;
}

结果图:

5.2使用类模板去实现一个数据结构

实现一个顺序栈模板

首先我们使用一下多文件编程,类似于c的那种,我们会发现问题如下:

main.cpp文件:

#include <iostream>
#include "socket.h"
using namespace std;
int main()
{
    socket<int> s(2);
    return 0;
}

stack.h文件:

#ifndef SOCKET_H
#define SOCKET_H
using namespace std;
#include <iostream>
template <class T>
class socket
{
    T* m_date;
    int len;
    int max_size;
public:
    //构造
    socket(int _len);
    //析构
    ~socket();
    //入栈
    void push(const socket& other);
    //出栈
    void out();
    //获取栈顶的值
    T get_out();
    //判断是否为空
    bool is_empty();
};
#endif // SOCKET_H

stack.cpp文件:

#include "socket.h"
template <class T>
socket<T>::socket(int _max_size)
{
    this->m_date=new T[len];
    this->len=0;
    this->max_size=_max_size;
}

结果图:

结果分析:如图所以,结果告诉我们无法连接到构造函数,这是由于我们使用的是模板类,模板类需要被编译两次,如果我们像这样把stack.cpp和stack.h分开写的话,stack.cpp里面的模板只被编译了一次,所以我们无法连接到构造函数。

使分文件编程的方式实现一个模板栈:

在C++分文件编程时,在业内常用的一种文件标准是后缀为.hpp的模板文件。

代码实现:

stack.cpp文件:

#ifndef STACK_HPP
#define STACK_HPP
using namespace std;
#include <iostream>
template  <class T>
class Stack
{
    T* my_data;
    int len;
    int max_size;
public:
    //构造函数
    Stack(int _max_size);
    //析构函数
    ~Stack();
    //入栈
    void push(const int& other);
    //出栈
    void out_data();
    //获取栈顶的值
    T get_data();
    //判断是否为空
    bool is_empty();
};
#endif // STACK_HPP
template <class T>
Stack<T>::Stack(int _max_size)
{
    this->my_data=new int[_max_size];
    this->len=0;
    this->max_size=_max_size;
}
template <class T>
Stack<T>::~Stack()
{
    if(this->my_data!=nullptr){
        delete this->my_data;
    }
}
template <class T>
void Stack<T>::push(const int& other)
{
    if(this->max_size<len){
        return;
    }
    my_data[len]=other;
    ++(this->len);
}
template <class T>
void Stack<T>::out_data()
{
    if(len<=0){
        return;
    }
    --(this->len);
}
template <class T>
T Stack<T>::get_data()
{
    return this->my_data[len-1];
}
template <class T>
bool Stack<T>::is_empty()
{
    if(this->len==0){
        return true;
    }
    return false;
}

main.cpp文件:

#include <iostream>
#include "stack.hpp"
using namespace std;
int main()
{
    Stack<int> s(12);
    s.push(1);
    s.push(2);
    s.push(3);
    while(!s.is_empty()){
        cout<<s.get_data()<<endl;
        s.out_data();
    }
    return 0;
}

结果图:

分析:因为这次我们把函数的实现放在了hpp文件里面,当我们调用头文件的时候问编译一次,还有就是当调用声明的时候也会编译一次,所以就达到了类模板的使用要求,所以这次我们就可以链接到。

5.3类模板的特化

#include <iostream>
using namespace std;
template<class T1>
class A
{
public:
    A()
    {
        cout<<"A的基础模板"<<endl;
    }
};
template <>
class A <int>
{
public:
    A()
    {
        cout<<"A的特化模板"<<endl;
    }
};
template <class T2,class T3>
class B
{
public:
  B()
  {
      cout<<"B的基础模板"<<endl;
  }
};
template <class T2>
class B<T2,float>
{
public:
    B()
    {
        cout<<"B的偏化模板"<<endl;
    }
};
int main()
{
  A<float> a;
  A<int> a1;
  B<int ,int> b1;
  B<int ,float> b2;
    return 0;
}

结果图:

分析:当使用类模板去定义对象时,因为具体指定使参数类型,所以他将优先调用与之指定类型相匹配的特化或偏特化版本。否则,将直接调用全特化。

5.4C++中类模板中的内嵌类

内嵌类一般情况下是为外围类而服务:比如说:STL容器中所提供的迭代器就是一种内嵌类。内嵌类并不是对外公开的,只做为外围类的一个辅助类。隐藏在外围类的内部,对外不可以见。只能通过::域名访问的形式,才能访问到。

代码实例:

#include <iostream>
using namespace std;
template <typename  T>
class A
{
public:
    class B
    {
        int a = 100;
        int b = 200;
        static B* c;
    };
};
template <class T>
typename A<T>::B* A<T>::B::c =  nullptr;
int main()
{
    A<int> a;
    cout << sizeof (a) << endl;
    A<float>::B b1;
    cout << sizeof(b1) <<endl;
    return 0;
}

内嵌类需要注意的几点内容:

1.内嵌类可以访问定义在外围类(enclosing class)中的静态实例变量。外围类不可以访问嵌套类的成员.

2.不能从内嵌类中访问外部类的非静态成员.

3.可以在外部通过作用域限定符调用.

到此这篇关于C++函数模板与类模板相同与不同介绍的文章就介绍到这了,更多相关C++函数模板与类模板内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ 类模板、函数模板全特化、偏特化的使用

    一.类模板全特化.偏特化 #pragma once #include <iostream> #include <map> template <typename T, typename U> class TC { public: TC() { std::cout << "泛化版本构造函数" << std::endl; } void funtest() { std::cout << "泛化版本成员函数&quo

  • C++ 函数模板和类模板详情

    目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式化 2.3 函数模板原理 2.4 函数模板实例化 2.5 模板参数的匹配原理 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数? 在C++中可以用到函数重载 class A { public: void Swap(int& x1,int& x2) { int temp=x1; x1=x2; x2=temp; } void Swap(double&

  • 浅析C++函数模板和类模板

    目录 一.函数模板 1.函数模板的定义和使用 2.函数模板的编译原理 3.函数模板的声明 二.类模板 1.类模板的定义和使用 2.类模板的编译原理 3.类模板的继承和派生 C++语言全盘继承了C语言的标准库,其中包换非常丰富的系统函数,例如输入/输出函数.数学函数.字符串处理函数和动态内存分配函数等.C++语言另外又增加了一些新的库,我们把C++语言新增的这部分库称为C++标准库.C++语言的模板技术包括函数模板和类模板.模板技术是一种代码重用技术,函数和类是C++语言中两种主要的重用代码形式.

  • C++类模板与函数模板基础详细讲解

    目录 函数模板 类模板 总结 函数模板 当我们想要定义一个可以支持泛型的函数时,就要采用函数模板的方式了.所谓泛型就是可以支持多种类型的操作,比如我们定义一个compare操作,他可以根据传递给他的参数类型动态调用对应的函数版本,实现多种类型的比较. template <typename T> int compare(const T &v1, const T &v2) { if (v1 < v2) return -1; if (v2 < v1) return 1;

  • C++模板基础之函数模板与类模板实例详解

    泛型编程  如果让你编写一个函数,用于两个数的交换.在C语言中,我们会用如下方法: // 交换两个整型 void Swapi(int* p1, int* p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } // 交换两个双精度浮点型 void Swapd(double* p1, double* p2) { double tmp = *p1; *p1 = *p2; *p2 = tmp; }  因为C语言不支持函数重载,所以用于交换不同类型变量的函数的函数名是不

  • C++函数模板与类模板实例解析

    本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解.具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思.泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库). 模板(template)是泛型编程的基础,一个模板就是一个创建类或函数的蓝图或公式.例如,当使用一个vector这样的泛型类型或者find这样的泛型函数

  • C++函数模板与类模板相同与不同介绍

    目录 1.模板 1.1何为模板 1.2C++的模板的形式有两种 1.3如何定义一个函数模板 1.4语法形式 1.5模板的编译机制 2.函数模板 2.1调用方式 2.2函数模板的特化与调用优先级 3.可变参函数模板 3.1概念 3.2代码实现(实现一个c中的printf的函数) 4.类模板 4.1类模板的定义形式 4.2代码实例 5.类模板中的特殊属性的初始化方式及继承与多态 5.1代码实例 5.2使用类模板去实现一个数据结构 5.3类模板的特化 5.4C++中类模板中的内嵌类 1.模板 1.1何

  • C++ 类模板与成员函数模板示例解析

    目录 类模板 类模板与成员函数模板的区别 类模板 前面以函数模板为例,介绍了具体化与实例化.那么对于类模板,有什么不同呢? 类包括成员变量和成员函数,他们都可以包含类模板的模板参数.而成员函数本身也可以是函数模板.看下面的两个类: // 类模板 template <typename T> class A { private: T t; public: void funcA(T t); }; template <typename T> void A<T>::funcA(T

  • C++类模板与模板类深入详解

    1.在c++的Template中很多地方都用到了typename与class这两个关键字,而且有时候二者可以替换,那么是不是这两个关键字完全一样呢? 事实上class用于定义类,在模板引入c++后,最初定义模板的方法为:template<class T>,这里class关键字表明T是一个类型,后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字,它的作用同class一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式了:      t

  • C++中的类模板详解及示例

    C++中的函数模板 对于类的声明来说,也有同样的问题.有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类: 复制代码 代码如下: 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;}; 其作用是

  • C++泛型编程函(数模板+类模板)

    目录 一.函数模板 1.函数模板介绍 2.函数模板与重载函数的关系 3.函数模板实现机制 二.类模板 1.类模板基本语法 2.类模板内函数的整体布局[分文件使用类模板] 3.类模板的static与模板类的static 4.数组实现万能容器 前言: 由于C++是静态语言,也就是说使用一个数据的时候必须先指定类型,这样的操作在编译后变量的类型是无法轻易改变的,就导致扩展性太差.或者一个函数需要很多次重载的时候,代码显得冗杂,由此产生了C++函数模板. 一.函数模板 1.函数模板介绍 ① 函数模板的产

随机推荐