【C++核心编程】函数模版&类模版

🔥博客主页: 我要成为C++领域大神

🎥系列专栏【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】

❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于分享知识,欢迎大家共同学习和交流。

C++除了有面向对象的编程思想,还有泛型编程的思想,泛型编程可以体现在C++的模版上。模板(Templates)是一种强大的泛型编程工具,允许我们去编写通用的类和函数,以支持多种数据类型,而无需重复编写代码。模板可以分为函数模板类模板两种主要类型。

在C++98添加关键字typename之前,C++使用关键字class来创建模板。
如果考虑向后兼容,函数模板应使用typename,而不是class。
函数模板实例化可以让编译器自动推导,也可以在调用的代码中显式的指定。

函数模板(Function Templates)

函数模板(Function Template)是C++中一种特殊的机制,允许我们编写通用的函数定义,这些函数可以用于多种不同的数据类型,而无需为每种数据类型编写单独的函数。 比如说,我们通常写的函数都是具体有形参类型,返回类型,里面具体数据类型的。类型参数化可以在我们写函数时将这些数据类型变成未知的变量,从而得到一个函数模板,我们可以根据传入不同的数据类型得到不同的结果和实现。

函数模板允许定义一个函数,其中某些类型可以作为参数进行通用化。定义函数模板使用 template 关键字和 <...> 括号来指定泛型参数。

语法:template <typename T>

示例:

template <typename T>
T add(T a, T b) {
    return a + b;
}
int main() {
    int sumInt = add<int>(3, 5);         // 实例化为 int 类型的 add 函数
    double sumDouble = add<double>(1.1, 2.2); // 实例化为 double 类型的 add 函数
    
    cout << "int两数之和: " << sumInt << endl;
    cout << "double两数之和: " << sumDouble << endl;
    
    return 0;
}

如何得到具体的函数?

编译器自动推导
    add(a, b);

上面表示让编译器根据我们传进去的数据自己推导T应该成为什么数据类型。

显式的指定
   add<int>(a, b);

上面表示显式指定数据类型,我们可以在实参列表前加上声明指定T的数据类型。

函数模板的注意事项

1)可以为类的成员函数创建模板,但不能是虚函数和析构函数。

2)使用函数模板时,必须明确数据类型,确保实参与函数模板能匹配上。

template<typename T>
void add()
{
}
int main()
{
	//Swap();//错误
	add<int>();
	return 0;
}

3)使用函数模板时,推导的数据类型必须适应函数模板中的代码。

不能模板传递不同的数据类型,这样编译器会不知道选择哪一种数据类型。

template<typename T>
void add(T& a, T& b)
{
}
 
int main()
{
	char a = 'a';
	int b = 20;
	add(a,b);
	return 0;
}

4)使用函数模板时,如果是自动类型推导,不会发生隐式类型转换,如果显式指定了函数模板的数据类型,可以发生隐式类型转换。

5)函数模板支持多个通用数据类型的参数。

函数模板支持重载,并且可以与非通用数据类型的参数一起使用。

  1. 规则1:当普通函数和函数模板都可以调用时,优先调用普通函数。
  2. 规则2:可以在函数调用时加上空的模板参数列表 <> 来强制调用函数模板。例如:add<>(...)
  3. 规则3:函数模板可以重载,即不同模板函数可以使用相同的函数名。
  4. 规则4:如果有更好的匹配,编译器会根据更好的匹配优先调用函数模板。
#include <iostream>
using namespace std;

void add(int a) {
    cout << "普通函数" << endl;
}

template<typename T>
void add(T a) {
    cout << "函数模板" << endl;
}

template<typename T>
void add(T a, T b) {
    cout << "函数模板2" << endl;
}

int main() {
    add(100);       // 调用普通函数,规则1
    add<>(100);     // 调用函数模板,规则2
    add(100, 200);  // 传入两个参数,优先匹配调用函数模板2,模板函数发生重载,规则3,4
    return 0;
}
  • add(100); 调用普通函数,因为规则1优先匹配普通函数。
  • add<>(100); 强制调用函数模板,因为规则2使用空模板参数列表。
  • add(100, 200); 传入两个参数,调用重载的函数模板2,因为规则3和规则4使得编译器选择更好的匹配。

类模板(Class Templates)

类模板与函数模板的作用类似,也需要在类定义的上面加上templatetypename,但在定义对象时,必须使用<>显式的指定模版类型

template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};

下面是一个简单的类模板的创建:

#include<iostream>
using namespace std;

template<class G, class T>
class Student
{
public:
	Student(G a, T b)
	{
		this->m_age = a;
		this->m_name = b;
	}
	G m_age;
	T m_name;

};
int main()
{
	Student<int, string> A(20, "蔡徐坤");
	cout <<"年龄:" << A.m_age << ",姓名:" << A.m_name << endl;
	return 0;
}

类模版的注意事项

注意:

1)在创建对象的时候,必须指明具体的数据类型。

2)使用类模板时,数据类型必须适应类模板中的代码。

3)类模板可以为通用数据类型指定缺省的数据类型(C++11标准的函数模板也可以)。

4)模板类的成员函数可以在类外实现。

5)可以用new创建模板类对象。

6)在程序中,模板类的成员函数使用了才会创建。

模版类具体化

模板类具体化(也称特化、特例化)有两种形式:完全具体化和部分具体化。具体化程度高的类优先于具体化程度低的类,具体化的类优先于未具体化的类。具体化的模板类中,成员函数在类外实现的代码应放在源文件中。

示例程序:

#include <iostream>         // 包含头文件。
using namespace std;        // 指定缺省的命名空间。

// 类模板
template<class T1, class T2>
class MathOperations {      // 类模板
public:
    T1 a;
    T2 b;

    MathOperations(const T1 a, const T2 b) : a(a), b(b) { 
        cout << "类模板:构造函数。\n"; 
    }

    void add() const {
        cout << "类模板:加法结果 = " << a + b << endl;
    }

    void multiply() const {
        cout << "类模板:乘法结果 = " << a * b << endl;
    }
};

// 类模板完全具体化
template<>
class MathOperations<int, double> {
public:
    int a;
    double b;

    MathOperations(const int a, const double b) : a(a), b(b) { 
        cout << "完全具体化:构造函数。\n"; 
    }

    void add() const {
        cout << "完全具体化:加法结果 = " << a + b << endl;
    }

    void multiply() const {
        cout << "完全具体化:乘法结果 = " << a * b << endl;
    }
};

// 类模板部分具体化
template<class T1>
class MathOperations<T1, int> {
public:
    T1 a;
    int b;

    MathOperations(const T1 a, const int b) : a(a), b(b) { 
        cout << "部分具体化:构造函数。\n"; 
    }

    void add() const {
        cout << "部分具体化:加法结果 = " << a + b << endl;
    }

    void multiply() const {
        cout << "部分具体化:乘法结果 = " << a * b << endl;
    }
};

int main()
{
    // 具体化程度高的类优先于具体化程度低的类,具体化的类优先于未具体化的类
    MathOperations<int, double> op1(3, 4.5);   // 将使用完全具体化的类
    MathOperations<char, int> op2('A', 10);    // 将使用部分具体化的类
    MathOperations<int, float> op3(7, 2.5);    // 将使用模板类

    op1.add();
    op1.multiply();

    op2.add();
    op2.multiply();

    op3.add();
    op3.multiply();

    return 0;
}

我们定义了一个通用的类模板 MathOperations,并提供了两个具体化版本:

  1. 完全具体化:为 MathOperations<int, double> 提供了一个特化版本。
  2. 部分具体化:为 MathOperations<T1, int> 提供了一个特化版本,其中 T1 可以是任意类型。

main 函数中:

  • MathOperations<int, double> op1(3, 4.5); 使用了完全具体化的类版本,并调用了 addmultiply 方法,输出对应的结果。
  • MathOperations<char, int> op2('A', 10); 使用了部分具体化的类版本,并调用了 addmultiply 方法,输出对应的结果。
  • MathOperations<int, float> op3(7, 2.5); 使用了通用的类模板版本,并调用了 addmultiply 方法,输出对应的结果。

模板类与继承

1)模板类继承普通类(常见)。

#include <iostream>
using namespace std;

class Base {
public:
    int m_base;
    Base(int base) : m_base(base) { cout << "调用了Base的构造函数。\n"; }
    void showBase() { cout << "调用了showBase()函数:m_base = " << m_base << endl; }
};

template<class T1, class T2>
class MathOperations : public Base {
public:
    T1 m_x;
    T2 m_y;
    MathOperations(const T1 x, const T2 y, int base) : Base(base), m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
    void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};

int main() {
    MathOperations<int, string> mo(8, "我是一个参数。", 3);
    mo.show();
    mo.showBase();
    return 0;
}

2)普通类继承模板类的实例化版本。

#include <iostream>
using namespace std;

template<class T1, class T2>
class MathOperations {
public:
    T1 m_x;
    T2 m_y;
    MathOperations(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
    void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};

class Derived : public MathOperations<int, string> {
public:
    int m_base;
    Derived(int base, int x, string y) : MathOperations(x, y), m_base(base) { cout << "调用了Derived的构造函数。\n"; }
    void showBase() { cout << "调用了showBase()函数:m_base = " << m_base << endl; }
};

int main() {
    Derived d(3, 8, "我是一个参数。");
    d.show();
    d.showBase();
    return 0;
}

3)普通类继承模板类。(常见)

#include <iostream>
using namespace std;

template<class T1, class T2>
class MathOperations {
public:
    T1 m_x;
    T2 m_y;
    MathOperations(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
    void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};

template<class T1, class T2>
class Derived : public MathOperations<T1, T2> {
public:
    int m_base;
    Derived(int base, const T1 x, const T2 y) : MathOperations<T1, T2>(x, y), m_base(base) { cout << "调用了Derived的构造函数。\n"; }
    void showBase() { cout << "调用了showBase()函数:m_base = " << m_base << endl; }
};

int main() {
    Derived<int, string> d(3, 8, "我是一个参数。");
    d.show();
    d.showBase();
    return 0;
}

4)模板类继承模板类。

#include <iostream>
using namespace std;

template<class T1, class T2>
class MathOperations {
public:
    T1 m_x;
    T2 m_y;
    MathOperations(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了MathOperations的构造函数。\n"; }
    void show() const { cout << "调用了show()函数:x = " << m_x << ", y = " << m_y << endl; }
};

template<class T, class T1, class T2>
class AdvancedMathOperations : public MathOperations<T1, T2> {
public:
    T m_z;
    AdvancedMathOperations(const T z, const T1 x, const T2 y) : MathOperations<T1, T2>(x, y), m_z(z) { cout << "调用了AdvancedMathOperations的构造函数。\n"; }
    void showAdvanced() { cout << "调用了showAdvanced()函数:z = " << m_z << endl; }
};

int main() {
    AdvancedMathOperations<int, int, string> amo(3, 8, "我是一个参数。");
    amo.show();
    amo.showAdvanced();
    return 0;
}

5)模板类继承模板参数给出的基类(不能是模板类)。

#include <iostream>
using namespace std;

class Base1 {
public:
    Base1() { cout << "调用了Base1的构造函数。\n"; }
    Base1(int a) { cout << "调用了Base1的构造函数Base1(int a)。\n"; }
};

class Base2 {
public:
    Base2() { cout << "调用了Base2的构造函数。\n"; }
    Base2(int a) { cout << "调用了Base2的构造函数Base2(int a)。\n"; }
};

template<class T>
class MathOperations : public T {
public:
    MathOperations() : T() { cout << "调用了MathOperations的构造函数MathOperations()。\n"; }
    MathOperations(int a) : T(a) { cout << "调用了MathOperations的构造函数MathOperations(int a)。\n"; }
};

int main() {
    MathOperations<Base1> mo1;
    MathOperations<Base2> mo2(5);
    return 0;
}

模板类与函数

模板类可以用于函数的参数和返回值,有三种形式:
1)普通函数,参数和返回值是模板类的具体实例。

#include <iostream>
using namespace std;

template<class T1, class T2>
class Test {
public:
    T1 m_x;
    T2 m_y;
    Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
    void display() const { cout << "display()  x = " << m_x << ", y = " << m_y << endl; }
};

// 普通函数,参数和返回值是模板类Test的具体实例。
Test<int, string> process(Test<int, string>& mo) {
    mo.display();
    cout << "调用了process(Test<int, string> &mo)函数。\n";
    return mo;
}

int main() {
    Test<int, string> mo(3, "this is a test");
    process(mo);
    return 0;
}

2) 函数模板,参数和返回值是某种模板类

#include <iostream>
using namespace std;

template<class T1, class T2>
class Test {
public:
    T1 m_x;
    T2 m_y;
    Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
    void display() const { cout << "display()  x = " << m_x << ", y = " << m_y << endl; }
};

// 函数模板,参数和返回值是模板类Test的某种类型。
template <typename T1, typename T2>
Test<T1, T2> process(Test<T1, T2>& mo) {
    mo.display();
    cout << "调用了process(Test<T1, T2> &mo)函数。\n";
    return mo;
}

int main() {
    Test<int, string> mo(3, "this is a test");
    process(mo);
    return 0;
}

3) 函数模板,参数和返回值是任意类型

#include <iostream>
using namespace std;

template<class T1, class T2>
class Test {
public:
    T1 m_x;
    T2 m_y;
    Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
    void display() const { cout << "display()  x = " << m_x << ", y = " << m_y << endl; }
};

// 函数模板,参数和返回值是任意类型。
template <typename T>
T process(T &mo) {
    mo.display();
    cout << "调用了process(T &mo)函数。\n";
    return mo;
}

int main() {
    Test<int, string> mo(3, "this is a test");
    process(mo);
    return 0;
}

模板类与友元

模板类的友元函数有三种类型:

1)非模板友元:友元函数不是模板函数,而是利用模板类参数生成的具体函数。

#include <iostream>
using namespace std;

template<class T1, class T2>
class Test {
    T1 m_x;
    T2 m_y;
public:
    Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
    friend void show(const Test<T1, T2>& a) {
        cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
    }
};

int main() {
    Test<int, string> a(88, "this is a test");
    show(a);

    Test<char, string> b('A', "this is a test");
    show(b);
}

2)约束模板友元

模板类实例化时,每个实例化的类对应一个友元函数。

#include <iostream>
using namespace std;

template <typename T>
void show(T& a);

template<class T1, class T2>
class Test {
    friend void show<>(Test<T1, T2>& a);
    T1 m_x;
    T2 m_y;
public:
    Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};

template<class T1, class T2>
class OtherTest {
    friend void show<>(OtherTest<T1, T2>& a);
    T1 m_x;
    T2 m_y;
public:
    OtherTest(const T1 x, const T2 y) : m_x(x), m_y(y) { }
}

template <typename T>
void show(T& a) {
    cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}

template <>
void show(Test<int, string>& a) {
    cout << "具体Test<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}

template <>
void show(OtherTest<int, string>& a) {
    cout << "具体OtherTest<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}

int main() {
    Test<int, string> a1(88, "this is a test");
    show(a1);  // 使用具体化版本

    Test<char, string> a2('A', "this is a test");
    show(a2);  // 使用通用版本

    OtherTest<int, string> b1(88, "this is a test");
    show(b1);  // 使用具体化版本

    OtherTest<char, string> b2('B', "this is a test");
    show(b2);  // 使用通用版本
}

3)非约束模板友元

模板类实例化时,会生成多个友元函数,每个实例化的类都拥有多个友元函数。

#include <iostream>
using namespace std;

template<class T1, class T2>
class Test {
    template <typename T> friend void show(T& a);
    T1 m_x;
    T2 m_y;
public:
    Test(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};

template <typename T> 
void show(T& a) {
    cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}

template <> 
void show(Test<int, string>& a) {
    cout << "具体<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}

int main() {
    Test<int, string> a(88, "this is a test");
    show(a);  // 使用具体化版本

    Test<char, string> b('A', "this is a test");
    show(b);  // 使用通用版本
}
  • 31
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值