【C++进阶笔记一】函数模板与类模板

    科技2022-08-04  105

    【C++进阶】(1)函数模板的声明及使用

    一、函数模板 `template < class T>`1. 简单的函数模板示例2. 实现通用数据类型排序算法示例3. 能过空模板参数列表 强制调用模板函数4. 模板函数 也可 以实现函数重载5. 模板的局限性(具体化的模板函数优先调用) 二、类模板 `template< typename T1, typename T2>`1、类模板代码示例2、类模板函数类外实现3、类模板分文件编写4、友元函数 类内 及 类外实现

    最近在搞FFmpeg 音视频开发,后面大量涉及音视频图形等界面开发(比如视频播放器), 利用C语言也可以实现 ,但相对麻烦,在这方面C++会方便很多,记得上一次学C++还是在大二的时候了, 刚好也算趁着这一次机会,把C++ 的知识全部重新捡起来巩固一遍, 也算逼自已一把,实现由C转C++,后面写代码、写算法就全使用C++ 来实现了。

    有关C++ 的学习,打算专门花两天时间全部过一遍,因为有基础,所以我学习时都是跳着学,这样快些 参考视频教程:《C++教程从0到1入门编程》

    第一天 10月4号 将C++基础全部过了一遍 (面向对象), 包括C++中的数据类型、数组、指针、类和对象等,和C大同小异,这里就不多说了。

    第二天 10月5号 将C++更高级一些编程过一遍(泛型编程思想),这些功能很多都是C中没有的,用起来确实会很方便。

    好了,前言就到这里吧,我们开始学习。

    一、函数模板 template < class T>

    有关知识点 ,我全部以注释的形式写在代码中,结合代码方便理解运用

    1. 简单的函数模板示例

    函数模板声明: template <typename T>,T 是一个通用数据类型,将数据类型参数化,利高代码的复用性 typename 也可以写成 template<class T>,没什么差别

    函数模板使用有两种方法: (1)方法一:利用编译器自动推导(必须推导出一致的数据类型 T 才可以使用) (2)方法二:显示指定类型 T,在显示指定类型后,转参如果不是当前类型,则会自动发生隐式类型转换。

    示例如下:

    #include <iostream> using namespace std; // 声明一个函数模板,告诉编译器后面代码中紧跟着的T不要的报错 // T 是一个通用数据类型,将数据类型参数化,利高代码的复用性 template<typename T> // 交换两个数据,数据类型为 T,数据传递方式为引用 void Swap(T &a, T &b){ T temp = a; a = b; b = temp; } int main(){ int a = 10, b = 20; cout << "初始值: a = " << a << " b = " << b << endl; // 利用函数模板交换数据 // 方法一:利用编译器自动推导 cout << "方法一、利用编译器自动推导 T 的数据类型" << endl; Swap(a, b); cout << "交换后: a = " << a << " b = " << b << endl << endl; // 方法二:显示指定类型 T cout << "方法二 、显示指定类型 T" << endl; Swap<int>(a, b); // 指定T 的数据类型为 T cout << "交换后: a = " << a << " b = " << b << endl; system("pause"); return 0; }

    2. 实现通用数据类型排序算法示例

    #include <iostream> using namespace std; // 实现通用的对数组从小到大排序的函数 template <class T> void Sort_array(T arr[], int len) { // 选择排序 for (int i = 0; i < len; i++){ // 外循环 int max = i; for (int j = i + 1; j < len; j++) // 内循环 { if (arr[max] > arr[j]) { // 比较出最小值索引 max = j; } } if (max != i) { // 交换元素 T temp = arr[i]; arr[i] = arr[max]; arr[max] = temp; } } } template<class T> void print_array(T arr[], int len){ for (int i = 0; i < len; i++){ cout << arr[i] << " "; } cout << endl << endl; } int main() { { char c_arr[] = "zyxwvutsrqponmlkjihgfedcba"; int num = sizeof(c_arr) / sizeof(char) - 1; // 对数组进行从小到大排序 Sort_array(c_arr, num); print_array(c_arr, num); // 打印数组 } { float f_arr[] = { 10.1,9.1,8.1,7.1,6.1,5.1,4.1,3.1,2.1,1.1 }; int num = sizeof(f_arr) / sizeof(float); // 对数组进行从小到大排序 Sort_array(f_arr, num); print_array(f_arr, num); // 打印数组 } system("pause"); return 0; }

    3. 能过空模板参数列表 强制调用模板函数

    当普通函数,与 模板函数重名时,如果要强制调用模板函数,需要使用增加空模板参数列表来实现:Sort_array<>(c_arr, num);

    代码示例如下, 如果不使用Sort_array<>(c_arr, num);来调用, 默认会调用普通函数Sort_array,从而报程序错误(函数未实现), 使用空模板参数列表后,正常运行,调用的是模板函数。

    #include <iostream> using namespace std; void Sort_array(char arr[], int len); // 实现通用的对数组从小到大排序的函数 template <class T> void Sort_array(T arr[], int len) { // 选择排序 for (int i = 0; i < len; i++) // 外循环 { int max = i; for (int j = i + 1; j < len; j++) // 内循环 { if (arr[max] > arr[j]) { // 比较出最小值索引 max = j; } } if (max != i) { // 交换元素 T temp = arr[i]; arr[i] = arr[max]; arr[max] = temp; } } } template<class T> void print_array(T arr[], int len) { for (int i = 0; i < len; i++) { cout << arr[i] << " "; } cout << endl << endl; } int main() { { char c_arr[] = "zyxwvutsrqponmlkjihgfedcba"; int num = sizeof(c_arr) / sizeof(char) - 1; // 对数组进行从小到大排序 Sort_array<>(c_arr, num); print_array(c_arr, num); // 打印数组 } { float f_arr[] = { 10.1,9.1,8.1,7.1,6.1,5.1,4.1,3.1,2.1,1.1 }; int num = sizeof(f_arr) / sizeof(float); // 对数组进行从小到大排序 Sort_array<>(f_arr, num); print_array(f_arr, num); // 打印数组 } system("pause"); return 0; }

    4. 模板函数 也可 以实现函数重载

    示例如下:

    template <class T> void fun(T &a, T &b ){ XXX; } template <class T> void fun(T &a, T &b , T &c){ XXX; } template <class T> void fun(T &a, T &b , T &c, int d){ XXX; }

    5. 模板的局限性(具体化的模板函数优先调用)

    模板并不是万能的,有些特这下的数据类型,需要用具体化方式来实现(比如一个 Class Person 对象)。 示例: template<> bool Compare(Person& a, Person& b)

    代码如下:

    #include <iostream> #include <string> using namespace std; class Person { public: Person(string name, int age) { this->m_Name = name; this->m_Age = age; } string m_Name; int m_Age; }; template<class T> bool Compare(T& a, T& b) { if (a == b) return true; return false; } // 虽然可能通过重载 operate== 来实现,但这个比较麻烦 // 此处,利用具体化Person 的版本实现代码,具体化模板函数优先调用 template<> bool Compare(Person& a, Person& b) { if (a.m_Name == b.m_Name && a.m_Age == b.m_Age) return true; else return false; } int main() { Person p1("Tom", 10); Person p2("Tony", 12); if (Compare(p1, p2)) cout << "p1 == p2" << endl; else cout << "p1 != p2" << endl; system("pause"); return 0; }

    二、类模板 template< typename T1, typename T2>

    函数模板是在模板后面写一个函数,类模板就是下在模板后写一个类,其他的没什么区别。

    类型板在可以指定N 个所需的可变数据类型,如下T1 、T2

    template<typename T1, typename T2>template<class T1, typename T2> 类 类

    注意,类模板中不能自动推导数据类型,必须显示指定数据类型,但在模板参数列表中可以使用默认参数。

    1、类模板代码示例

    #include <iostream> #include <string> using namespace std; template<class NameType, class AgeType = int> class Person { public: Person(NameType name, AgeType age) { this->m_Name = name; this->m_Age = age; } void showPerson(){ cout << "name=" << this->m_Name << " age=" << this->m_Age <<endl; } NameType m_Name; AgeType m_Age; }; int main() { Person<string, int> p1("Tom", 10); p1.showPerson(); Person<char, char> p2('a', 80); // 显示指定数据类型 p2.showPerson(); Person<string> p3("Tom", 111); // 使用默认数据类型 p3.showPerson(); system("pause"); return 0; }

    2、类模板函数类外实现

    #include <iostream> #include <string> using namespace std; // 类模板 父类 template<class T> class Base { T m; }; // 子类必须是类模板才能继承父类 template<class T, class t=int> class Person : public Base<T> { public: Person(T name, t age) { this->m_Name = name; this->m_Age = age; } Person(T name, t tage, int sum); void showPerson(int sum); void showPerson() { cout << "name=" << this->m_Name << " age=" << this->m_Age << endl; } T m_Name; t m_Age; }; // 重载构造函数,类外实现 template<class T, class t> Person<T, t>::Person(T name, t age, int sum) { cout << "name=" << name << " age=" << age << " sum=" << sum << endl; } // 普通类方法,类外实现 template<class T, class t> void Person<T, t>::PshowPerson(int sum) { cout << "name=" << this->m_Name << " age=" << this->m_Age << endl; } int main() { Person<string, int> p1("Tom", 10); p1.showPerson(); Person<char, char> p2('a', 80); // 显示指定数据类型 p2.showPerson(); Person<string> p3("Tom", 111); // 使用默认数据类型 p3.showPerson(); Person<string, int> p4("Tom", 10, 1000); system("pause"); return 0; }

    3、类模板分文件编写

    类模板分文件编写时,有如下两种方式:

    类模板定义代码保存为.h,类模板函数实现代码保存为 .cpp,此时需要 #include "xxx.cpp"将类模板定义代码 和类模板函数实现代码 保存在一起,名为 xxx.hpp,此时需要包含 #include "xxx.hpp"

    4、友元函数 类内 及 类外实现

    #include <iostream> #include <string> using namespace std; // 声明 Person 类,让 showPerson2 知道 template<class T, class t = int> class Person; // 声明并实现 showPerson2 方法 template<class T, class t> void showPerson2(Person<T,t> & p) { cout << "name=" << p.m_Name << " age=" << p.m_Age << endl; } // 子类必须是类模板才能继承父类 template<class T, class t=int> class Person { // 友元函数类内实现 friend void showPerson(Person& p) { cout << "name=" << p.m_Name << " age=" << p.m_Age << endl; } // 友元函数类外实现 friend void showPerson2<>(Person &p); public: Person(T name, t age) { this->m_Name = name; this->m_Age = age; } T m_Name; t m_Age; }; int main(){ Person<string, int> p1("Tom", 10); showPerson2(p1); system("pause"); return 0; }
    Processed: 0.012, SQL: 8