C++学习笔记:09 函数模板与类模板
引入
考虑一个求绝对值函数myabs,对于int,double,float等数据类型需要重载多次,编写多个函数体。函数体逻辑代码完全一致,仅仅是数据类型不同。
在这种情况下,可以使用函数模板
#include <iostream>
using namespace std;
int myabs(int x) { return x >= 0 ? x : -x; }
float myabs(float x) { return x >= 0 ? x : -x; }
double myabs(double x) { return x >= 0 ? x : -x; }
int main() {
cout << myabs(1) << endl;
cout << myabs(1.1) << endl;
cout << myabs(1.2) << endl;
return 0;
}
myabs()
函数可以使用模板函数申明
template<class T>
T myabs(T x) { return x >= 0 ? x : -x; }
函数模板
函数模板定义语法
template <模板参数表>
函数定义
模板参数表的内容:
- 类型参数:class(或typename) 标识符
- 常量参数:类型说明符 标识符
- 模板参数:template <参数表> class 标识符
注意
一个函数模板并非自动可以处理所有类型的数据
只有能够进行函数模板中运算的类型,可以作为类型实参
自定义的类,需要重载模板中的运算符,才能作为类型实参
例如:
include <iostream>
using namespace std;
template <class T> //定义函数模板
void outputArray(const T* array, int count) {
for (int i = 0; i < count; i++)
cout << array[i] << " ";
cout << endl;
}
int main() {
const int A_COUNT = 8, B_COUNT = 8, C_COUNT = 20;
int a[A_COUNT] = { 1, 2, 3, 4, 5, 6, 7, 8 };
double b[B_COUNT] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 };
char c[C_COUNT] = "Welcome!";
cout << " a array contains:" << endl;
outputArray(a, A_COUNT);
cout << " b array contains:" << endl;
outputArray(b, B_COUNT);
cout << " c array contains:" << endl;
outputArray(c, C_COUNT);
return 0;
}
/*
a array contains:
1 2 3 4 5 6 7 8
b array contains:
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8
c array contains:
W e l c o m e !
*/
类模板
类模板的作用
使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值,能取任意类型(包括基本类型的和用户自定义类型)。
类模板的声明
类模板:
template <模板参数表>
class 类名{
类成员声明
};
如果需要在类模板以外定义其成员函数,则要采用以下的形式:
template <模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)
注意:
- 模板类的声明与定义需要放在同一文件内
- 模板类中重载<<,只能在类体里面实现,在类外写定义不好使
#include <iostream>
using namespace std;
struct Student {
int id;
float gpa;
public:
friend ostream& operator <<(ostream& os, const Student& in);
};
ostream& operator << (ostream& os, const Student& in) {
os << "ID:" << in.id << " " << "GPA:" << in.gpa << endl;
return os;
}
template<class T>
class Store {
private:
T item;
bool haveValue;
public:
Store();
T& getElem();
void putElem(const T& e);
};
template<class T>
Store<T>::Store() {
this->haveValue = false;
}
template<class T>
T& Store<T>::getElem() {
if (!haveValue) {
cout << "No item present!" << endl;
}
return this->item;
}
template<class T>
void Store<T>::putElem(const T& e) {
this->haveValue = true;
this->item = e;
}
int main() {
Store<int> s1, s2;
s1.putElem(3);
s2.putElem(-7);
cout << s1.getElem() << " " << s2.getElem() << endl;
Student g = { 1000, 23 };
Store<Student> s3;
s3.putElem(g);
cout << s3.getElem() << endl;
Store<double> d;
cout << "Retrieving object D... ";
cout << d.getElem() << endl;
return 0;
}
/* output
3 -7
ID:1000 GPA:23
Retrieving object D... No item present!
-9.25596e+61
*/
例子
Array数组类
#include <iostream>
#include <iomanip>
using namespace std;
template<class T>
class Array {
private:
T* list;
int size;
public:
Array(int sz = 50);
Array(const Array<T>& a);//复制构造函数
~Array();
Array<T>& operator = (const Array<T>& rhs); //overload =
T& operator [](int i); //overload []
const T& operator [](int i) const; //overload []
operator T* ();
operator const T* () const;
int getSize() const;
void resize(int sz);
void fill(T in) {
for (int i = 0; i < this->size; i++)
this->list[i] = in;
}
friend ostream& operator <<(ostream& os, const Array<T>& e) {
os << "size of array = " << e.size << endl;
for (int i = 0; i < e.size; ++i)
os << e.list[i] << " ";
os << endl;
return os;
}
};
template<class T>
Array<T>::Array(int sz) {
this->size = sz;
this->list = new T[this->size]{};
}
template<class T>
Array<T>::~Array() {
delete[] this->list;
}
template<class T>
Array<T>::Array(const Array<T>& a) {
this->size = a.size;
this->list = new T[this->size];
for (int i = 0; i < this->size; ++i) {
this->list[i] = a.list[i];
}
}
template<class T>
Array<T>& Array<T>::operator =(const Array<T>& rhs) {
if (&rhs != this) {
if (this->size != rhs.size) {
delete[] this->list;
size = rhs.size;
list = new T[this->size];
}
for (int i = 0; i < this->size; ++i)
this->size[i] = rhs.size[i];
}
return *this;
}
template<class T>
T& Array<T>::operator [](int i) {
return this->list[i];
}
template<class T>
const T& Array<T>::operator [](int i) const {
return this->list[i];
}
//重载指针转换运算符
template<class T>
Array<T>::operator T* () {
return list;
}
template<class T>
Array<T>::operator const T* () const {
return list;
}
template<class T>
int Array<T>::getSize() const {
return this->size;
}
template<class T>
void Array<T>::resize(int sz) {
if (sz == this->size) return;
T* newlist = new T[sz]{};
int n = (sz < this->size) ? sz : this->size;
for (int i = 0; i < n; ++i)
newlist[i] = this->list[i];
delete[] this->list;
list = newlist;
this->size = sz;
}
int main() {
Array<int> a(10);
int n, count = 0;
cout << "Enter a value >= 2 as upper limit for prime numbers: ";
cin >> n;
//求给定范围内的质数
for (int i = 2; i <= n; i++) {
bool isPrime = true;
for (int j = 0; j < count; j++)
//若i被a[j]整除,说明i不是质数
if (i % a[j] == 0) {
isPrime = false; break;
}
if (isPrime) {
if (count == a.getSize())
a.resize(count * 2);
a[count++] = i;
}
}
cout << a;
return 0;
}
/*
Enter a value >= 2 as upper limit for prime numbers:20
size of array = 10
2 3 5 7 11 13 17 19 0 0
*/
以上例子中重载指针运算符
//重载指针转换运算符
template<class T>
Array<T>::operator T* () {
return list;
}
template<class T>
Array<T>::operator const T* () const {
return list;
}
/*
后置const是为了让常对象能调用这个成员函数(常对象只能调用常成员函数,不能调用普通成员函数);“const T*”表示,通过对象名创建动态数组后,通过对象名不能改变数组
*/
为什么要重载指针运算符?参考链接
void read(int* p, int n) {
for (int i = 0; i < n; i++)
cin >> p[i];
}
int main() {
Array<int> a(10);
read(a, 5);
cout << a;
return 0;
}
调用read函数并传入Array类型指针时,因为read函数只接受int类型指针,所以会自动搜索从Array型指针到int型指针的转换方式,如果能搜索到,就执行转换,把转换后的指针交给read函数,搜索不到,编译器就会报错。
虽然重载了指针转换运算符,但是其作用只是为了能把对象名a当类内动态数组名list一样,传入类外函数的参数表作形实结合(实参是对象名,形参是T型指针);
光重载指针转换运算符还不够,要想对象名能像数组名一样使用下标,还要重载下标运算符。
如果接着在main函数写a[i],编译器是不会把这里的a当作数组名,所以仅仅是写a[i]是无法调用数组元素的(编译器不认这种代码),只有重载了下标运算符“[ ]”,编译器才会把a[i]当成list[i];
总结:
重载指针运算符,作用仅限于把“a”转换成“list”;重载下标运算符,作用仅限于把“a[i]”转换成“list[i]”;
C++学习笔记:09 函数模板与类模板的更多相关文章
- C++学习之函数模板与类模板
泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思.泛型编程最初诞生于C++中,目的是为了实现C++ ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十一章:模板测试
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十一章:模板测试 代码工程地址: https://github.co ...
- JavaSE学习笔记(8)---常用类
JavaSE学习笔记(8)---常用类 1.Object类 java.lang.Object类是Java语言中的根类,即所有类的父类.它中描述的所有方法子类都可以使用.在对象实例化的时候,最终找的父类 ...
- 转:学习笔记: Delphi之线程类TThread
学习笔记: Delphi之线程类TThread - 5207 - 博客园http://www.cnblogs.com/5207/p/4426074.html 新的公司接手的第一份工作就是一个多线程计算 ...
- 机器学习实战(Machine Learning in Action)学习笔记————09.利用PCA简化数据
机器学习实战(Machine Learning in Action)学习笔记————09.利用PCA简化数据 关键字:PCA.主成分分析.降维作者:米仓山下时间:2018-11-15机器学习实战(Ma ...
- C++ GUI Qt4学习笔记09
C++ GUI Qt4学习笔记09 qtc++ 本章介绍Qt中的拖放 拖放是一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式.除了剪贴板提供支持外,通常它还提供数据移动和复制的功 ...
- Kotlin学习笔记(9)- 数据类
系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步! Kotlin学习笔记(1)- 环境配置 Kotlin学习笔记(2)- 空安 ...
- C++进阶-1-模板基础(函数模板、类模板)
C++进阶 模板 1.1 函数模板 1 #include<iostream> 2 using namespace std; 3 4 // 模板 5 6 // 模板的简单实例 7 // 要求 ...
- Matlab学习笔记 figure函数
Matlab学习笔记 figure函数 matlab中的 figure 命令,能够创建一个用来显示图形输出的一个窗口对象.每一个这样的窗口都有一些属性,例如窗口的尺寸.位置,等等.下面一一介绍它们. ...
- matlab学习笔记 bsxfun函数
matlab学习笔记 bsxfun函数 最近总是遇到 bsxfun这个函数,前几次因为无关紧要只是大概看了一下函数体去对比结果,今天再一次遇见了这个函数,想想还是有必要掌握的,遂查了些资料总结如下. ...
随机推荐
- ProjectEuler 007题
题目:By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is ...
- Java程序设计学习笔记(二)
--正则表达式 正则表达式 ^ 匹配的开始 $ 匹配的结束 [] 表示匹配任意一个字符 [asdasd] ...
- 【SpringMVC】域对象共享数据
使用ModelAndView向request域对象共享数据 index.html <a th:href="@{/testModelAndView}">使用ModelAn ...
- JS方式实现隐藏手机号码中间4位数
1.截取 function test (value) { const start = value.slice(0, 3) const end = value.slice(-4) return `${s ...
- centos7 配置 ftp 服务器(本地用户)
2021-09-02 1. 安装 # 安装 vsftpd yum -y install vsftpd 2. 启动服务并添加到开机自启 # 启动 vsftpd systemctl start vsftp ...
- 整理之Service
Service 基础 一个Service的基本结构 class MyService : Service() { private val mBinder = MyBinder() override fu ...
- git clone 设置临时的 proxy
export ALL_PROXY=socks5://127.0.0.1:1086 git clone --depth 1 https://github.com/xxx/xxx.git unset AL ...
- 微信小程序 image 组件 src 请求不能设置 header 的问题
只能先 wx.downloadFile 得到 tempFilePath,然后设置 src = tempFilePath
- python库--jieba(中文分词)
import jieba 精确模式,试图将句子最精确地切开,适合文本分析:全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义:搜索引擎模式,在精确模式的基础上,对长词再次切 ...
- JEECG代码审计之文件上传
JEECG代码审计之文件上传 0x01 简述 JEECG(J2EE Code Generation)是一款基于代码生成器JEE的智能开发平台.引领新的开发模式(Online Coding->代码 ...