您的位置:首页 > 编程语言 > C语言/C++

C++学习笔记:09 函数模板与类模板

2021-09-15 14:12 429 查看

课程《C++语言程序设计进阶》清华大学 郑莉老师)

引入

考虑一个求绝对值函数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]”;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: