第五章 模板与群体数据(1)

引入

整数类型和浮点数类型求绝对值的算法,需要写两种重载函数吗?

int abs(int x) {
    return x < 0 ? -x : x;
}

double abs(double x) {
    return x < 0 ? -x : x;
}

答案是不需要的

函数模板

就像制造业的模板一样,设定一个模板,大体的事情就被框定了

针对函数来讲,既然干的事情都是一样的,那么这件事就被框定了,变化的部分就是模板外的修饰

template <typename T>            // 1
T abs(T x) {                     // 2
    return x < 0 ? -x : x;
}

1):typename 可以换成关键字 class; T 是自定义名字,可以设定为比较有意义的

2):依据模板参数由编译器自动生成代码

注意:不是简单的宏替换,当然你可以看作是宏替换来简单理解模板实际代码

基本数据类型 

如果可以推导出 T 是基本数据类型,那么一般的cout << 这类插入运算符没有问题

自定义数据类型

特别要注意处理插入运算符,需要重载该运算符,否则没有办法“替换”

示例:

template <class T>
void outputArray(const T *array, int count) {
    for(int i=0; i<cout; i++)
        cout << array[i] << " ";
    cout << endl;
}

// 使用部分
int a[8] = {1,2,3,4,5,6,7,8};
double b[8] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8};
char c[20] = "Welcome";

// 基本数据类型,可以自动推导出数据类型
outputArray(a, 8);
outputArray(b, 8);
outputArray(c, 20);

// 自定义类型,比如类,需要提供 << 运算符重载

注意事项 

  • 一个函数模板并非自动可以处理所有类型的数据
  • 只有能够进行函数模板中运算的类型,可以作为类型实参
  • 自定义的类,需要重载模板中的运算符,才能作为类型实参

类模板 

既然函数能做成模板,那么类为什么不可以呢?

使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员,某些成员函数的参数,某些成员函数的返回值,能取多种不同类型(包括基本类型的和用户自定义类型)

template <模板参数表>

class 类模板名

{ 类成员声明 }

  • 模板参数表中参数可以声明为该模板类的友元类
  • 可以通过 typedefusing 对实例化的类模板定义别名

模板的默认实参 

函数/类 模板可以有默认模板实参

template <typename T = double>

示例:

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

struct Student {
	int id;
	float gpa;
};

template <class T>
class Store {
private:
	T item;
	bool haveValue;
public:
	friend T;
	Store();
	T &getElem();
	void putElem(const T &x);
};

template <class T>
Store<T>::Store():haveValue(false) {}

template <class T>
T &Store<T>::getElem() {
	if(!haveValue) {
		cout << "No item present!" << endl;
		exit(1);
	}

	return item;
}

template <class T>
void Store<T>::putElem(const T &x) {
	haveValue = true;
	item = x;
}

int main() {
	using IntStore = Store<int>;        // 1
	IntStore s1, s2;
	s1.putElem(3);
	s2.putElem(-7);
	cout << s1.getElem() <<" " << s2.getElem() << endl;
	
	Student g = {1000, 23};
	Store<Student> s3;                  // 2
	s3.putElem(g);
	cout << "The student id is " << s3.getElem().id << endl;
	
	Store<double> d;
	cout << "Retrieving object D...";
	cout << d.getElem() << endl;
	
	return 0;
}

1):使用 using 方式可以定义别名

2):不实用 using 方式

再次说明,可以看作是 宏替换,但具体是编译器做替换动作,否则这个文件会出现重复定义类的情况,这也说明了不是简单 宏替换的做法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值