C和C++程序员面试秘笈之⑩

本系列博客基于董山海的<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 &lt; T Y P E L I S T , A R G L I S T &gt; F u n c t i o n D e f i n i t i o n template&lt;TYPE_{LIST},ARG_{LIST}&gt;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;
				}
			}
		}
	}
};
  1. Sort 成员为 static,可用 Test::Sort 访问;
  2. 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值