C++ 泛型编程详解

一、模板函数

1、模板:模板是泛型编程的重要思想,也是C++的精髓之一,C++的STL库完全通过模板实现(关于STL有兴趣的可以去研究一下这个开源项目:[https://github.com/Alinshans/MyTinySTL]),对比函数重载,函数模板只需要通过一次函数定义就可以实现不同参数列表和参数类型的函数重载功能,下面是个简单的模板函数。

#include <iostream>
#include <typeinfo>

using namespace std;

template<typename T, typename Y>
void tfunc_1(T &t, Y &y)
{
    cout << "t:" << typeid(t).name() << " " << t << endl;
    cout << "y:" << typeid(y).name() << " " << y << endl;
}
T tfunc_2(T &t)
{
    t = t + 1;
    return t;
}

int main()
{
    int n = 2;
    double f = 3.5;
    tfunc_1(n, f);
    cout << tfunc_2(n) << endl;
    return 0;
}

// 运行结果:
// t:i 2
// y:d 3.5
// 3
// 说明:使用typeid().name()返回变量类型,i表示int,d表示double,依此类推.

2、函数模板具体化:函数模板具体化是指如果要将某一个或某几个要处理的数据类型进行单独处理,需要额外定义对应数据类型的模板函数,形式是“ template <> void fun(type &t); ”,函数模板具体化和普通函数可同时存在,调用顺序为——普通函数 > 函数模板具体化 > 模板函数,这就类似于在类之外实现"多态"。下面给出一个函数模板具体化的例子。

#include <iostream>
#include <typeinfo>

using namespace std;

struct Node
{
    int val;
    Node *next;
    Node(int x) : val(x), next(NULL) {}
};

// 模板函数
template <typename T>
void tfunc(T &t)
{
    cout << "tT:" << t << endl;
}

// 函数模板具体化(用于处理Node类型)
template <>
void tfunc(Node &node)
{
    cout << "tNode val:" << node.val << endl;
}

// 普通函数
void tfunc(int &a)
{
    cout << "tfunc():" << a << endl;
}

int main()
{
    double a = 2.1;
    tfunc(a);
    int b = 1;
    tfunc(b);
    Node node(2);
    tfunc(node);
    
    return 0;
}

// 输出:
// tT:2.1     
// tfunc():1  
// tNode val:2

3、函数模板实例化:让编译器生成指定类型函数定义,不用定义函数实现,实例化示例为“ template void fun(type &t); ”,下面是一个简单的例子。(详细可参考[https://www.cnblogs.com/cthon/p/9203234.html])

// 说明:在函数调用时可直接显示实例化,而不适用显示实例化声明.
#include <iostream>
#include <typeinfo>

using namespace std;

template <typename T>
void tfunc(T &t)
{
    cout << "tT:" << typeid(t).name() << " " << t << endl;
}

int main()
{
    char i = 'A';
    tfunc<char>(i);		// 或写成 template void tfunc<char>(i);
    return 0;
}

// 输出:tT:c A

二、类模板

1、类模板可以指定默认模板参数(函数模板不可以),跟函数参数的默认值一样,必须从右向左连续赋值默认类型,如果实例化对象时又传递了类型,则默认类型会被覆盖掉,跟函数参数是一样的。创建对象时需要传递模板参数列表,模板参数列表加在类名后面ClassName ; 如果类的模板参数列表有默认值,可以不传模板参数,但一定要加 <> 如 ClassName< > classN; 创建堆区对象的时候,所有的类名称后面都要加模板参数列表,如 ClassName< typename T >* classN = new ClassName< typename T>; 除了类内,其他地方出现 ClassName 的地方一般都要加模板参数列表。下面是个简单的类模板示例。

#include <iostream>
#include <typeinfo>

using namespace std;

template <typename T = int, typename Y = char>
class Test
{
private:
    T t;
    Y y;
public:
    Test(T t, Y y): t(t), y(y) {}
    void tfunc();
};

template <typename T, typename Y>   // 类模板的函数在类外实现,需要加上模板参数列表,但不需要加指定的默认模板参数
void Test<T, Y>::tfunc()            // 类外使用Test需要加模板参数
{
    cout << t << " " << y << endl;
}

int main()
{
    int n = 2;
    double d = 2.1;
    Test<int, double> test(n, d);
    // 使用默认模板参数:Test<> test(int(2), char('a'));
    test.tfunc();
    return 0;
}

// 输出:2 2.1

2、类模板的继承:类模板被继承后参数的传递方式主要有两种,一种直接在子类继承父类的时候,为父类指定固定的类型,二是通过子类模板参数列表传递。下面是一个类模板继承的简单示例。

template <typename T, typename Y>
class A
{
public:
    A(T t, Y y) {}
};

class Test: public A<int, double>  // 父类是类模板,子类是普通类
{
public:
    Test(): A<int, double>(2, 2.1) {}
};

main()
{
    Test();
}

/*****************************************/

template <typename T1, typename Y1>
class B
{
public:
    B(T1 t) {}    
};

template <typename X, typename Z, typename P> // 父类为类模板,子类为类模板
class Test: public A<X, P>
{
public:
    Test(X x, Z z, P p): A<X, P>(x) {}
};

main()
{
    Test<int, double, char>(int(2), double(2.1), char('a'));
}

更多继承关系参考:[http://c.biancheng.net/view/324.html]

3、类模板的多态:在创建对象时,分为子类没有模板(CFather<short, char>*cf = new CSon;)和子类有模板(CFather<short, char> *cf = new CSon<short, int, char>)两种,子类和父类的模板参数列表可以不一样,但一定要对应好。下面是个简单的示例。

#include <iostream>
using namespace std;

template<typename T, typename Y>
class A
{
public:
    virtual void tfunc(T t, Y y) = 0;
};

class Test: public A<int, double> 
{
public: 
    virtual void tfunc(int n, double d)
    {
        cout << n << " " << d << endl;
    }
};

// 父类是类模板,子类是普通类,在多态情况下只有父类需要指定模板参数
int main()
{
    A<int, double> *a = new Test;
    a->tfunc(2, 2.1);
    return 0;
}

// 输出:2 2.1
#include <iostream>
using namespace std;

template<typename T, typename Y>
class A
{
public:
    virtual void tfunc(T t, Y y) = 0;
};

template <typename X, typename Z, typename P>
class Test : public A<X, P>
{
public:
    virtual void tfunc(X x, P p)
    {
        cout << x << " " << p << endl;
    }
};

// 父类是类模板,子类是类模板,在多态情况下父类和子类都需要指定模板参数
int main()
{
    A<int, double> *a = new Test<int, char, double>;
    a->tfunc(2, 2.1);
    return 0;
}

// 输出:2 2.1

4、类模板具体化:类模板具体化分为部分具体化和全部具体化,如下.

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class Test
{
public:
    Test()
    {
        cout << "T1 and T2" << endl;
    }
};
// 部分具体化
template <typename T1>
class Test<T1, int>
{
public:
    Test()
    {
        cout << "T1 and int" << endl;
    }
};
// 部分具体化
template <typename T2>
class Test<long, T2>
{
public:
    Test()
    {
        cout << "long and T2" << endl;
    }
};
// 全部具体化
template <>
class Test<long, int>
{
public:
    Test()
    {
        cout << "long and int" << endl;
    }
};

// 父类是类模板,子类是类模板,在多态情况下父类和子类都需要指定模板参数
int main()
{
    Test<char, char>();
    Test<char, int>();
    Test<long, char>();
    Test<long, int>();
    return 0;
}

/* 输出:
 * T1 and T2
 * T1 and int
 * long and T2
 * long and int
 */

三、成员模板

成员模板简单来讲就是模板中的模板,常见于模板类中构建模板函数,详细可参考:[https://blog.csdn.net/luoshabugui/article/details/104619151]。下面给出一个简单的示例:

#include <iostream>

class Base1 {};
class Base2 {};
class Test1 : public Base1 {};
class Test2 : public Base2 {};
template <typename T1, typename T2>
class Pair
{
public:
    T1 t1;
    T2 t2;
    Pair(T1 t1, T2 t2) : t1(t1), t2(t2) {}
    // 类模板中的成员模板
    template <typename U1, typename U2>
    Pair(const Pair<U1, U2> &pair) : t1(pair.t1), t2(pair.t2) {}
};
int main()
{
    Pair<Base1 *, Base2 *>(Pair<Test1 *, Test2 *>(new Test1, new Test2));
    return 0;
}
原文地址:https://www.cnblogs.com/horacle/p/13701314.html