C++泛型编程——模板

泛型编程是编写独立于任何特定类型的代码,利用模板实现编译期多态。在C++中,模板用于创建函数和类,允许在编译时为不同数据类型生成代码。模板的实例化发生在函数调用时,且函数指针只能指向实例化的函数。类模板可以派生出新的类,包括类模板和非模板类。
摘要由CSDN通过智能技术生成
模板
泛型编程
  • 泛型编程 是以 独立于任何特定类型的 方式编写 代码 ,使用 泛型 程序时,需要提供具体程序实例所 操作的类型 或值。
  • 在面向对象C++编程中, 是OO三大特性之一,这种多态称为运行期多态,也称为动态多态;在 编程 中,多态基于template (模板)的具现化与函数的重载解析,这种 编译 期进行,因此称为 编译 静态
  • 需要注意到, 在编译器编译模板函数时,并不生成任何目标代码 !而是等到主函数调用模板函数时,即当类型参数的含义明确后,编译器将以模板函数为样例,分别生成两个函数。 这个过程也叫做函数模板的  “实例化” 并且,函数指针也只能指向模板的实例,而不能指向无目标代码的模板本身!
模板的结构
template < 模板形参表 >
返回值类型 函数名(形式参数列表)
{
函数体语句
}
template<typename T>/ template<class T>的区别

C++ 标准:
template<typename T> 用于基础数据类型, T可以是int, char 等
template<class T>  用于复制数据类型,T :string ,类等
实际情况:
二者都可以用于各种数据类型。
调用函数模板的过程:
1. 模板实参推断(template argument deduction):编译器根据 函数调用中所给出的实参的类型,确定相应的模板实参。
2. 函数模板的实例化(instantiation):模板实参确定之后,编译 器就使用模板实参代替相应的模板形参,产生并编译函数模板的一个 特定版本 (称为函数模板的一个实例(instance))(注意: 此过程中不进行常规隐式类型转换 )。
模板的特化
• 在模板当中有些 特殊的类型,当想要针对特 殊的类型进行一些 特殊的操作 ,这时候就可以用模板的特化
• 在正常的模板下面接着编写代码,写一个空 的 template<> 然后写个具体的函数代码来补充
• 如实例所示,当传入的实参类型是int类型, 就执行模板的特化部分,而非int类型执行正常的模板推断
template <typename T>
void Swap( T& v1, T& v2)
{
T temp;
temp = v1;
v1 = v2;
v2 = temp;
}
template <>
void Swap( int & v1, int & v2)
{
int temp;
temp = v1;
v1 = v2;
v2 = temp;
v1 += 10;
v2 += 10;
}
对函数模板进行重载:
• 定义名字相同而函数形参表不同的函数模板
• 或者定义与函数模板同名的非模板函数 (正常函数 ),在其函 数体中完成不同的行为
函数调用的静态绑定规则(重载协议):
1. 如果某一同名非模板函数(指 正常的函数)的形参类型正好与函数调用的实参类型 匹配(完全一致),则调用该函数。否则,进入第2步
2. 如果能从同名的函数模板 化一个函数实例,而该函数实例的形参类型正好与 函数调用的实参类型匹配( 完全一致 ),则调用该函数模板的实例函数。否则,进入第3步
• 在步骤2中: 首先匹配函数模板的 特化,再匹配非指定特殊的函数模板
3. 对函数调用的实参 进行隐式类型转换后非模板函数再次进行匹配,若能找到匹 配的函数则调用该函数。否则,进入第4步
4. 提示编译错误
注:
  • 在这里非const转化为const,不属于隐式转换
  • 函数模板是不进行隐式转换的,只有非函数模板才进行隐式转换!
类模板
/* 类模板一般形式 */
template <模板形参表>
class 类模板名
{
类成员声明
...
}
模板类:
就是类模板中的参数确定之后的产物,也就是类模板实例化后的产物。(它是一个参数已经确定好的类)
类模板的使用实际上是将类模板实例化成一个类
/* 在类模板外定义成员函数的一般形式 */
template <模板形参表>
返回值类型 类模板名<模板形参名列表>::函数名(函数形参表){
函数实现
...
}
模板的定义和实现都写在.h文件中。属于包含编译模式
非类型模板形参:
• 两类模板形参:类型模板形参和非类型模板形参
非类型模板形参:
• 相当于模板内部的常量
• 形式上类似于普通的函数形参
• 对模板进行实例化时,非类型形参由相应模板实参的值代替
• 对应的模板实参必须是编译时常量表达式
例:
template <typename ElementType, int N>
class Stack {
public:
Stack();
~Stack();
void push(ElementType obj);
void pop();
ElementType getTop() const;
bool isEmpty() const;
private:
/* 栈节点类型 */
ElementType elements[N];
int count;
};
int main()
{
Stack<int, 10> stack;
// 实例化一个保存int型元素的栈
for (int i = 1; i < 9; i++) // 向栈中压入8个元素
stack.push(i);
while (!stack.isEmpty()) {
// 栈不为空时循环
cout << stack.getTop() << " "; // 显示栈顶元素
stack.pop();
// 弹出栈顶元素
}
}
N由编译器计算得出
/* 非类型模板形参实例2 */
/* 非类型模板形参实例2 */

普通类的成员函数模板

不管是普通类还是类模板,它的成员函数可以是一个函数模板,称为“成员函数模板”,不可以是虚函数,否则编译器会报错。
class A
{
public:
    template<typename T>
    void myft(T temp)    //成员函数模板
    {
        cout << temp <<endl;
    }
};
   
int main()
{
    //普通类的成员函数模板
    //不管是普通类,还是类模板,它的成员函数可以是一个函数模板,称为“成员函数模板”,不可以是虚函数
    
    A a;
    a.myft(3);  //执行这句话的时候,才会实例化这个模板。
    cout << "Hello world!" << endl;
    return 0;
}

类模板的成员函数模板

template<typename C>
class A
{
public:
    template <typename T2>
    A(T2 v1, T2 v2);
   // {
   //构造函数我们也引入自己的模板,和整个类的模板C没关系
   //}
    template<typename T>
    void myft(T temp)    //成员函数模板
    {
        cout << temp <<endl;
    }
    void myfunc()  //普通成员函数
    {
    }
    C m_ic;
};
    template <typename C> // 先跟类的模板参数列表
    template <typename T2>  // 再跟构造函数自己的模板参数列表
    A<C>::A(T2 v1, T2 v2)
    {
        cout << v1 <<" "<<v2<< endl;
    }
int main()
{
    //类模板的成员函数模板
    //类模板的模板参数必须用<>指定,  成员函数模板(函数模板)的参数都可以推断。
    A<float>  a(1 , 2);
    A<float>  a2(1.2 , 2.2);
     //类模板的成员函数(包括普通成员函数、成员函数)只有为程序所用,才进行实例化
    //如果某函数从未使用,则不会实例化该成员函数。
    a.myft(9); //调用的时候才会实例化
    cout << "Hello world!" << endl;
    return 0;
}
注意:类模板的成员函数(包括普通成员函数、成员函数模板)只有为程序所用时(代码中出现了)才会实例化。如果某函数从未使用则不会实例化(.obj 中生成代码)该成员函数。 (也就是说如果代码中没有调用到类成员函数时,编译器编译生成的 .obj 文件中是没有这个成员函数代码的)
类模板的派生
类模板的派生
       1) 普通类派生类模板
  2) 类模板也可以派生类模板,这时,派生类模板的参数表中应包含 基类模板的参数
  3) 模板类与普通类一样也具有多继承,即模板类之间允许有多继承。
可以从类模板派生出新的类,既可以派生类模板,也可以派生非模板类。派生方法:
⑴ 从类模板派生类模板可以从类模板派生出新的类模板,它的派生格式如下例所示:
template <class T>
class base
{
……
};
template <class T>
class derive:public base<T>
{
……
};
与一般的类派生定义相似,只是在 指出它的基类时要缀上模板参数,即 base<T>。
⑵ 从类模板派生非模板类 ,可以从类模板派生出非模板类,在派生中,作为非模板类的基类,必须是类模板实例化后的模板类,并且在定义派生类前不需要模板声明语句:template<class>。例如:
template <class T>
class base
{
……
};
class derive:public base<int>
{
……
};
在定义derive类时,base已实例化成了int型的模板类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值