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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM