本系列博客基于董山海的<C和C++程序员面试秘笈>,旨在记录,欢迎交流,可联系 zywang@shu.edu.cn !
第10章:泛型编程
1、面试题1
泛型编程:编写完全一般化并可重复使用的算法,其效率与针对特定数据类型而设计的算法一致。所谓泛型,是指具有在多种数据类型上皆可操作的含义。
int max(int a, int b) { //参数和返回值为int类型
return a > b ? a : b;
}
//
float max(float a, float b) { //参数和返回值为float类型
return a > b ? a : b;
}
//
double max(double a, double b) { //参数和返回值为double类型
return a > b ? a : b;
}
重载函数,我们可以使用模板:
template <class T> //class 也可用 typename 代替
T max(T a, T b) { //参数和返回值类型都是 T
return a > b ? a : b;
}
//这里的 class 不代表对象的类,而是类型
2、面试题2
函数模板与类模板的基本概念和区别:
函数模板:抽象的函数定义,代表一类的同构函数。通过用户提供的具体参数,C++编译器在编译时刻能够将模板实例化,根据同一模板创建出不同的具体函数。
函数模板:
t
e
m
p
l
a
t
e
<
T
Y
P
E
L
I
S
T
,
A
R
G
L
I
S
T
>
F
u
n
c
t
i
o
n
D
e
f
i
n
i
t
i
o
n
template<TYPE_{LIST},ARG_{LIST}>Function_{Definition}
template<TYPELIST,ARGLIST>FunctionDefinition
其中,
F
u
n
c
t
i
o
n
D
e
f
i
n
i
t
i
o
n
Function_{Definition}
FunctionDefinition为函数定义;
T
Y
P
E
L
I
S
T
TYPE_{LIST}
TYPELIST为类型参数表;
A
R
G
L
I
S
T
ARG_{LIST}
ARGLIST为变量表,相当于形式参数。
类模板:
更高层次的抽象类定义,用于使用相同代码创建不同的类模板的定义与函数模板的定义类似,只是把函数模板中的函数定义部分换做类说明,并对类的成员函数进行定义。
template<<模板参数表>>
class<类模板名> {
<类模板定义体>;
};
如:定义一个表示平面的点(point)类,这个类有两个成员变量,分别表示横坐标和纵坐标,并且这两个坐标的类型可以是 int、doubli、float等类型。因此可能写出类似 Point_int_int、Point_float_int、Point_float_float等这样的类。通过类模板我们只需要写一个类。
#include <iostream>
using namespace std;
//
template <class T1, class T2>
class Point_T {
public:
T1 a;
T2 b;
Point_T() :a(0), b(0) {} //默认构造函数
Point_T(T1 ta, T2 tb) :a(ta), b(tb) {} //带参数的构造函数
Point_T<T1, T2>& operator=(Point_T<T1, T2>&pt); //赋值函数
friend Point_T<T1, T2>operator+(Point_T<T1, T2>&pt1, Point_T<T1, T2>&pt2); //重载 +
};
//
template <class T1,class T2> //赋值函数
Point_T<T1, T2>& Point_T<T1,T2>::operator=(Point_T<T1, T2> &pt) {
this->a = pt.a;
this->b = pt, b;
return *this;
}
//
template <class T1, class T2> //重载+
Point_T<T1, T2>operator +(Point_T<T1, T2> &pt1, Point_T<T1, T2>&pt2) {
Point_T<T1, T2> temp;
temp.a = pt1.a + pt2.a; //结果对象中的 a 和 b 分别为两个参数对象的各自 a 与 b 之和
temp.b = pt1.b + pt2.b;
return temp;
}
//
template <class T1,class T2> //重载输出流操作符
ostream& operator<<(ostream& out, Point_T<T1, T2>& pt) {
out << "(" << pt.a << ","; //输出(a,b)
out << pt.b << ")";
return out;
}
//
int main() {
Point_T<int, int> intPt1(1, 2); //T1 和 T2 都是int
Point_T<int, int> intPt2(3, 4); //T1 和 T2 都是int
Point_T<float, float> floatPt1(1.1f, 2.2f); //T1 和 T2 都是float
Point_T<float, float> floatPt2(3.3f, 4.4f); //T1 和 T2 都是float
//
Point_T<int, int> intTotalPt;
Point_T<float, float> floatTotalPt;
//
intTotalPt = intPt1 + intPt2; //类型为 Point_T<int,int> 的对象相加
floatTotalPt = floatPt1 + floatPt2; //类型为 Point_T<float,float> 的对象相加
//
cout << intTotalPt << endl; //输出 Point_T<int,int> 的对象
cout << floatTotalPt << endl; //输出 Point_T<float,float> 的对象
system("pause");
return 0;
}
函数模板、模板函数、类模板、模板类:
函数模板:重点是模板,表示一个模板,用于生产函数;
模板函数:重点是函数;
类模板:重点是模板,用于生产类,如 Point_T 是类模板;
模板类:一个模板生成的类,如 Point_T<int, int>
3、面试题3
使用模板的缺点:
缺点:不当地使用模板会导致代码膨胀,即二进制代码臃肿而松散,会影响程序的运行效率;
解决:把 C++ 模板中与参数无关的代码分离出来;
4、面试题4
模板的特化:函数模板的特化和类模板的特化
(1)函数模板的特化:当函数模板需要对某些类型进行特别处理时,称为函数模板的特化。
bool IsEqual(T t1, T t2) {
return t1 == t2;
}
//
int main() {
char str1[] = "Hello";
char str2[] = "Hello";
cout << IsEqual(1, 1) << endl;
cout << IsEqual(str1, str2) << endl;
return 0;
}
代码 cout<<IsEqual(str1,str2)<<endl 比较字符串是否相等。由于 max 函数模板只是简单地比较传入参数的值,即两个指针是否相等。这与初衷不符合,因此 max函数模板需要对 char* 类型进行特别处理,即特化:
template <>
bool IsEqual(char* t1, char* t2) {
return strcmp(t1, t2) == 0;
}
(2)类模板的转化:
template <class T>
class compare {
public:
bool IsEqual(T t1, T t2) {
return t1 == t2;
}
};
//
int main() {
char str1[] = "Hello";
char str2[] = "Hello";
compare<int> c1;
compare<char *> c2;
cout << c1.IsEqual(1, 1) << endl;
cout << c2.IsEqual(str1, str2) << endl;
return 0;
}
这边进行类模板的特化:
template <class T>
class compare<char *> { //特化(char *)
public:
bool IsEqual(char* t1, char* t2) {
return strcmp(t1,t2)== 0;
}
};
5、面试题5
设计一个公共的 class,通过他的接口可以对任意类型的数组排序。
说明:
关键在于:对于任何类型的数组进行排序。以往我们是使用不同类型参数的重载函数来完成。
template <class T>
class Test {
public:
static void Sort(T *array, int len.bool(*Compare)(T& a, T& b)) {
T temp; //用于冒泡排序的交换
assert(len >= 1); //len 必须大于1
for (int i = 0; i < len - 1; i++) { //冒泡排序
for (int j = len - 1; j > i; j--) {
//使用Compare函数指针的方式进行比较
if (Compare(array[j], array[j - 1])) { //根据升序或降序需要进行交换
temp = array[j - 1];
array[j - 1] = array[j];
array[j] = temp;
}
}
}
}
};
- Sort 成员为 static,可用 Test::Sort 访问;
- Sort 的第3个参数Compare是一个函数指针,它指向两个函数模板的一种。
template <class T>
bool ascend(T& a, T& b) {
return a < b ? true : false;
}
template <class T>
bool descend(T& a, T& b) {
return a > b ? true : false;
}
我们可以使用 Test 类的 Sort 函数对任意类型的数组进行排序。但下面对于某个类的对象的数组排序,需要重载 > 与 < 操作符。
template <class T>
class MyRect {
public:
MyRect() :length(0), width(wid) {}
MyRect(T len,T wid):length(len),width(wid){}
T Area() {
return length * width;
}
private:
T length;
T width;
};
//
template <class T>
operator > (MyRect<T>& rect1, MyRect<T>& rect2) {
return rect1.Area() > rect2.Area() ? true : false;
}
//
template <class T>
operator < (MyRect<T>& rect1, MyRect<T>& rect2) {
return rect1.Area() < rect2.Area() ? true : false;
}