11. 模板

模板就是简历通用的模具,大大提高 复用性。

1.1 概念

特点:

  • 模板不可以直接使用,只是一个框架
  • 模板的通用并不是万能的

1.2 函数模板

泛型编程,主要利用的技术 是模板

两种模板:函数模板和类模板

1.2.1 函数模板用法

建立一个通用函数,返回类型和形参类型可以不具体制定,用一个虚拟的类型来代表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template<typename T>
// class167_模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}

int main()
{
int a = 10, b = 20;
double c = 1.1, d = 2.2;
//自动类型推导
mySwap(a, b);
cout << a << " " << b << endl;
//显式指定类型
mySwap<double>(c, d);
cout << c << " " << d << endl;
}

利用关键字template

两种方式:自动类型推导,显式指定类型

模板的目的是为了提高复用性,将类型参数化

1.2.2 函数模板注意事项

注意事项:

·自动类型推导,必须推导出一致的数据类类型T,才可以使用

·模板必须要确定出的数据类型,才可以使用

1.2.3 排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// class167_模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}

template<typename T>
void mySort(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
int max = i;
for (int j = i; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;
}
}
if (max!=i)
{
mySwap(arr[max], arr[i]);
}
}
}

template<class T>
void printArray(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test()
{
char charArr[] = "badcfe";
int length = sizeof(charArr) / sizeof(charArr[0]);
mySort(charArr, length);
printArray(charArr, length);
}
int main()
{
test();
}

1.2.4 普通函数与 函数模板的区别

普通函数与函数模板区别:

·普通函数调用时可以发生自动类型转换(隐式类型转换)

·函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

如果利用显示指定类型的方式,可以发生隐式类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

template<class T>
T myAdd(T a,T b)
{
return a + b;
}
void test()
{
cout << myAdd(1, 2) << endl;
cout << myAdd<int>(1, 'a') << endl;
cout << myAdd<char>(1, 'a') << endl;
}

int main()
{
test();
}

1.2.5 调用规则如下:

1.如果函数模板和普通函数都可以实现,优先调用普通函数

2.可以通过空模板参数列表来强制调用函数模板

3.函数模板也可以发生重载

4.如果函数模板可以产生更好的匹配,优先调用函数模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// class167_模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;
void myPrint(int a, int b)
{
cout << "hello" << endl;
}
template<class T>
void myPrint(T a, T b)
{
cout << "temp" << endl;
}
template<class T>
void myPrint(T a, T b,T c)
{
cout << "temp ccc" << endl;
}
void test()
{
//优先普通函数
myPrint(10, 10);

myPrint<>(10, 10);
myPrint<>('a', 'b');
//重载
myPrint<>(10, 10, 10);
}

int main()
{
test();
}

既然提供了 函数模板,最好 就不要提供普通函数,否则 会出现二义性

1.2.6 模板的 局限性

在上述代码中,如果T的数据类型传入的是像Perso这样的自定义数据类型,也无法正常运行

因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// class167_模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;
class Person
{
public:
Person(string m_name, int m_age) :name(m_name), age(m_age) {}
string name;
int age;
};
template<class T>
bool myCompare(T &a, T &b)
{
if (a==b)
{
return true;
}
else
{
return false;
}
}

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

void test()
{
int a = 10;
int b = 20;
cout << myCompare(a, b) << endl;

Person p1("Tom", 10);
Person p2("Tim", 10);
cout << myCompare(p1, p2) << endl;
//重载运算符
//具体化实现
}

int main()
{
test();
}

1.3 类模板

1.3.1 类模板语法

类模板作用:

·建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。

1
2
template <typename  T>
class

解释:

template-声明创建模板

typename-表面其后面的符号是一种数据类型,可以用class代替

T通用的数据类型,

名称可以替换,通常为大写字母

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// class174_类模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template<class NameType, class AgeType>
class Person
{
public:
Person(NameType n, AgeType a) :name(n), age(a) {}
void ShowPerson()
{
cout << name << endl;
cout << age << endl;
}
NameType name;
AgeType age;
};

int main()
{
Person<string, int> p1("Tom", 99);
p1.ShowPerson();
}

1.3.2 类模板与函数模板区别主要有两点:

1.类模板没有自动类型推导的使用方式

2.类模板在模板参数列表中可以有默认参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

template<class NameType, class AgeType = int>
class Person
{
public:
Person(NameType n, AgeType a) :name(n), age(a) {}
void ShowPerson()
{
cout << name << endl;
cout << age << endl;
}
NameType name;
AgeType age;
};

int main()
{
Person<string> p1("Tom", 99);
p1.ShowPerson();
}

1.3.3 类模板中成员函数创建时间

类模板中成员函数和普通类中成员函数创建时机是有区别的:

普通类中的成员函数一开始就可以创建

·类模板中的成员函数在调用时才创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// class174_类模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

class Person1
{
public:
void ShowPerson1()
{
cout << "name" << endl;
}

};

class Person2
{
public:
void ShowPerson2()
{
cout << "age" << endl;
}

};

template <class T>
class MyClass
{
public:
T obj;

void func1()
{
obj.ShowPerson1();
}
void func2()
{
obj.ShowPerson2();
}
};
int main()
{
MyClass<Person1>m;
m.func1();
//m.func2();
}

1.3.4 类模板对象做函数参数

学习目标:

·类模板实例化出的对象,向函数传参的方式

一共有三种传入方式:

1.指定传入的类型一直接显示对象的数据类型(最常用)

2.参数模板化

一将对象中的参数变为模板进行传递

3.整个类模板化

一将这个对象类型模板化进行传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// class174_类模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template <class T1, class T2>
class Person
{
public:
Person(T1 m_name, T2 m_age) :name(m_name), age(m_age) {}
void ShowPerson()
{
cout << name << endl;
cout << age << endl;
}
T1 name;
T2 age;
};
//指定传入类型
void printPerson1(Person<string, int>&p)
{
p.ShowPerson();
}
//2.参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
p.ShowPerson();
//cout << typeid(T1).name() << endl;
//cout << typeid(T2).name() << endl;
}
//3.整个类模板化
template<class T>
void printPerson3(T& p)
{
p.ShowPerson();
cout << typeid(T).name() << endl;

}
int main()
{
Person<string, int>p("sun", 100);
printPerson1(p);
Person<string, int>p2("zhu", 100);
printPerson2(p2);
Person<string, int>p3("tang", 100);
printPerson3(p3);
}

1.3.5 类模板与继承

当类模板碰到继承时,需要注意一下几点:

·当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型

·如果不指定,编泽器无法给子类分配内存

·如果想灵活指定出父类中T的类型,子类也需变为类模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// class174_类模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template <class T>
class Base
{
T m;
};

class Son :public Base<int>
{

};
template<class T1, class T2>
class Son2 :public Base<T2>
{
T1 obj;
};
int main()
{
Son s1;

Son2 <int, char> s2;
}

1.3.6 类模板函数类外实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// class174_类模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template <class T1, class T2>
class Person
{
public:
Person(T1 m_name, T2 m_age);
void ShowPerson();

T1 name;
T2 age;
};
template <class T1, class T2>
Person<T1,T2>::Person(T1 m_name, T2 m_age)
{
this->name = (m_name);
this->age = (m_age);
}
template <class T1, class T2>
void Person<T1, T2>::ShowPerson()
{
cout << this->name << endl;
cout << this->age << endl;
}

int main()
{
Person<string, int>p("sun", 100);
p.ShowPerson();
}

1.3.7 类模板份文件编写

问题:

类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

解决:

。解决方式1:直接包含.cpp源文件

。解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// class174_类模板.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

#include "Person.hpp"
using namespace std;

int main()
{
Person<string, int>p("sun", 100);
p.ShowPerson();
}

#pragma once
#include <iostream>
using namespace std;

template <class T1, class T2>
class Person
{
public:
Person(T1 m_name, T2 m_age);
void ShowPerson();

T1 name;
T2 age;
};
template <class T1, class T2>
Person<T1, T2>::Person(T1 m_name, T2 m_age)
{
this->name = (m_name);
this->age = (m_age);
}
template <class T1, class T2>
void Person<T1, T2>::ShowPerson()
{
cout << this->name << endl;
cout << this->age << endl;
}

1.3.类模板与友元

全局函数类内实现·直接在类内声明友元即可

全局函数类外实现·需要提前让编译器知道全局函数的存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#pragma once
#include <iostream>
using namespace std;

template <class T1, class T2>
class Person;

template <class T1, class T2>
void ShowPerson2(Person<T1, T2> p)
{
cout << p.name << endl;
cout << p.age << endl;
}
template <class T1, class T2>
class Person
{

friend void ShowPerson(Person<T1, T2> p)
{
cout << p.name << endl;
cout << p.age << endl;
}
friend void ShowPerson2 <>(Person<T1, T2> p);
public:
Person(T1 m_name, T2 m_age);

private:
T1 name;
T2 age;
};
template <class T1, class T2>
Person<T1, T2>::Person(T1 m_name, T2 m_age)
{
this->name = (m_name);
this->age = (m_age);
}



int main()
{
Person<string, int>p("sun", 100);
ShowPerson2(p);
ShowPerson(p);
}

11. 模板
http://binbo-zappy.github.io/2024/11/27/cpp/11-模板/
作者
Binbo
发布于
2024年11月27日
许可协议