(黑马C++)L08 C++模板

C++泛型编程使用模板技术来实现。

一、函数模板基本使用

函数模板就是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表,这个通用函数就称为函数模板。

template<class T>等价于template<typename T>

#include<iostream>
using namespace std;

//使用函数模板实现不同类型的数据交换
//类型参数化 泛型编程 -- 模板技术
template<class T> //告诉编译器出现T时不要报错,T是一个通用类型
void mySwap(T& a, T& b) {
    T tmp = a;
    a = b;
    b = tmp;
}

void test() {
    int a = 10;
    int b = 20;
    //1.自动推导类型,必须有参数类型才能推导
    mySwap(a, b);

    //2.显式指定类型
    mySwap<int>(a, b);

    //3.模板必须要指定T才能使用
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
}

int main() {
    test();
    system("pause");
    return 0;
}

二、实现通用的数组排序

使用函数模板实现对char和int类型数组进行排序。

//使用函数模板实现对char和int类型数组进行排序
//排序规则是从小到大,利用快速排序
#include <iostream>
#include <string>
using namespace std;

//先把元素自己排好位置,然后给左右数组排序
template<class T>
void mySort(T arr[], int low, int high) {
	if (low >= high) return;
	int i = low, j = high;
	int key = arr[low];
	while (i < j) {
		while (arr[j] >= key && i < j) j--;
		arr[i] = arr[j];
		while (arr[j] <= key && i < j) i++;
		arr[j] = arr[i];
	}
	arr[i] = key;
	mySort(arr, low, i - 1);
	mySort(arr, i + 1, high);
	return;
}

//打印数组
template<class T>
void printArray(T arr[], int len) {
	for (int i = 0; i < len; ++i) {
		cout << arr[i] << " ";
	}
	cout << endl;
	return;
}

//char类型测试
void test01() {
	char charArr[] = "helloworld";
	int len = sizeof(charArr) / sizeof(char);
	mySort(charArr, 0, len-1);
	printArray(charArr, len);
}

//int类型测试
void test02() {
	int intArr[] = { 4, 1, 7, 100, 35 };
	int len2 = sizeof(intArr) / sizeof(int);
	mySort(intArr, 0, len2 - 1);
	printArray(intArr, len2);
}

int main() {
	//test01();
	test02();
	system("pause");
	return 0;
}

三、函数模板与普通模板的区别及调用规则

函数模板和类模板的上面都要写template<class T>,后面必须要紧跟着函数或类,跟着的是函数,就是函数模板,跟着的是类,就是类模板。

1.普通函数与函数模板的区别

普通的函数可以进行隐式类型转换,函数模板不可以进行隐式类型转换。

2.普通函数和模板函数的调用规则

(1)如果出现重载,优先使用普通函数调用,如果普通函数只有声明没有实现,会报错。

(2)如果想强制调用模板,可以使用空参数列表,即myPrint<>(a, b)

(3)函数模板也可以发生重载(参数数量不同)。

(4)如果函数模板可以产生更好的匹配,那么优先调用函数模板。

四、模板机制剖析

(1)编译器并不是把函数模板处理成能处理任何类型的函数;

(2)函数模板通过具体类型产生不同的函数,通过模板生成的函数叫模板函数;

(3)编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

五、函数模板的局限性及解决

如果函数中有a==b的判断,但是此时a和b都是结构体,就无法实现。

解决办法:前面有基础函数,后面通过第三代具体化自定义数据类型来解决上述问题,如果具体化能够优先匹配,那么就选择具体化。

语法:template<> 返回值 函数名 <具体类型> (参数)

template<> bool myCompare<Person> (Person &a, Person& b) {
    if(a.m_Age == b.m_Age) {
        return true;
    }
    return false;
}

六、类模板的基本使用

//类模板
template <class NameType, class AgeType>
class Person{
public:
    Person(NameType name, AgeType age) {
        this->m_Name = name;
        this->m_Age = age;
    }
    
    NameType m_name;
    AgeType m_Age;
}

(1)类模板不支持自动类型推导,Person p("张三", 100)会报错,只能使用显式指定类型,

Person<string, int> p("张三", 100)。

(2)类模板可以有默认,template <class NameType, class AgeType = int>

七、成员函数创建时机

成员函数一开始不会创建,而是在运行时才会创建。

八、类模板做函数的参数

类模板做函数参数有以下几种方式:

  1. 指定传入类型
  2. 参数模板化
  3. 整体类型化
#include <string>
#include <iostream>
using namespace std;

template <class NameType, class AgeType>
class Person {
public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson() {
		cout << "姓名:" << this->m_Name << endl;
		cout << "年龄:" << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};

//类模板做函数的参数
//方式1 --  指定传入类型
void doWork(Person<string, int>& p) {
	p.showPerson();
}
void test01() {
	Person<string, int> p("YD", 20);
	doWork(p);
}

//方式2 -- 参数模板化
template<class T1, class T2>
void doWork2(Person<T1, T2>& p) {
	//如何查看类型
	cout << typeid(T1).name() << endl;
	p.showPerson();
}
void test02() {
	Person<string, int> p("YD", 20);
	doWork2(p);
}

//方式3 -- 整体模板化
template<class T>
void doWork3(T& p) {
	p.showPerson();
}
void test03() {
	Person<string, int> p("YD", 20);
	doWork3(p);
}

int main()
{
	//test01();
	//test02();
	test03();
	system("pause");
	return 0;
}

九、类模板碰到继承的问题以及解决

(1)在继承时如果基类是模板类,必须指定基类中T的类型,否则无法给T分配内存。

template <class T>
class Base {
public:
	T m_A;
};

//在继承时必须指定基类中T的类型,否则无法给T分配内存
class Child : public Base<int> {

};

(2) 可以由用户指定基类类型。

template <class T>
class Base {
public:
	T m_A; //double类型
};

//Child2也是模板
template<class T1, class T2>
class Child2 : public Base<T2>{
public:
	T1 m_B; //int类型
};

十、类模板的类外实现成员函数

//类外实现成员函数
template<class T1, class T2>
class Person {
public:
	Person(T1 name, T2 age);
	void showPerson();
	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "姓名:" << this->m_Name << endl;
	cout << "年龄:" << this->m_Age << endl;
}

十一、类模板的分文件编写问题以及解决

以下代码会出现无法解析的外部命令错误,因为C++中进行的是单元编译,导致.h头文件不会创建函数的实现,无法解析外部命令。

解决办法是将test.cpp中头文件 #include "Person.h" 替换为 #include "Person.cpp"(不推荐)。

建议模板不要做分文件编写,写在一个文件中进行声明和实现,最后把后缀名改为.hpp。

//Person.h文件
#include <iostream>
#include <string>
using namespace std;

template<class T1, class T2>
class Person {
public:
	Person(T1 name, T2 age);
	void showPerson();
	T1 m_Name;
	T2 m_Age;
};


//Person.cpp文件
#include "Person.h"

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "姓名:" << this->m_Name << endl;
	cout << "年龄:" << this->m_Age << endl;
}

//test.cpp
#include <iostream>
#include <string>
#include "Person.h"
using namespace std;

int main() {
    Person<string, int> p("XX", 10);
    p.showPerson();
    system("pause");
    return 0;
}

十二、友元函数遇到类模板

(1)友元函数在类内实现

#include <string>
#include <iostream>
using namespace std;

//友元函数是为了保护私有成员属性
template<class T1, class T2>
class Person {
	//友元函数在类内实现
	friend void printPerson(Person<T1, T2>& p) {
		cout << "姓名:" << p.m_Name << endl;
		cout << "年龄:" << p.m_Age << endl;
		return;
	}
public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};

void test01() {
	Person<string, int> p("TOM", 10);
	printPerson(p);
}

int main()
{
	test01();
	system("pause");
	return 0;
}

(2)友元函数在类外实现

#include <string>
#include <iostream>
using namespace std;

//让编译器提前看到printPerson的声明
//让编译器看到Person的声明
template<class T1, class T2> class Person;
template<class T1, class T2> void printPerson(Person<T1, T2>& p);

//友元函数是为了保护私有成员属性
template<class T1, class T2>
class Person {
	//友元函数在类内实现 利用空参数列表告诉编译器这是模板函数的声明
	friend void printPerson<>(Person<T1, T2>& p); //上面没模板,是普通函数
public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}

private:
	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
void printPerson(Person<T1, T2>& p) {
	cout << "姓名:" << p.m_Name << endl;
	cout << "年龄:" << p.m_Age << endl;
	return;
}

void test01() {
	Person<string, int> p("TOM", 10);
	printPerson(p);
}

int main()
{
	test01();
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值