赞
踩
群体数据:自定义类型的数据由多个基本类型或自定义类型的元素组成
群体类:对于群体数据,仅有系统预定义的操作是不够的,在很多情况下,还需要设计与某些具体问题相关的特殊操作,并按照面向对象的方法将数据与操作封装起来
- template <class 类型参数1, class类型参数2, ...>
- 返回值类型 模板名(形参表)
- {
- 函数体
- }
代码中的class也可换成typename
template<参数表>class标识符,指明可以接受一个类模板名作为参数
- template <class T>
- void Swap(T & x, T & y)
- {
- T tmp = x;
- x = y;
- y = tmp;
- }
T 是类型参数,代表类型。编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动地保留。同一个类型参数只能替换为同一种类型。编译器在编译到调用函数模板的语句时,会根据实参的类型判断该如何替换模板中的类型参数
- #include <iostream>
- using namespace std;
- template<class T>
- void Swap(T & x, T & y)
- {
- T tmp = x;
- x = y;
- y = tmp;
- }
- int main()
- {
- int n = 1, m = 2;
- Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
- double f = 1.2, g = 2.3;
- Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
- return 0;
- }
编译器由模板自动生成函数的过程叫模板的实例化。由模板实例化而得到的函数称为模板函数。在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误
编译器对模板进行实例化时,并非只能通过模板调用语句的实参来实例化模板中的类型参数,模板调用语句可以明确指明要把类型参数实例化为哪种类型。可以用:
模板名<实际类型参数1, 实际类型参数2, ...>
- #include <iostream>
- using namespace std;
- template <class T>
- T Inc(T n)
- {
- return 1 + n;
- }
- int main()
- {
- cout << Inc<double>(4) / 2; //好好看看这里是怎么用的
- return 0;
- }
此时,Inc返回的是2.5,不因为4是int而改变(因为指明了double),函数模板是不允许隐式类型转换的,调用时类型必须严格匹配
可以有不只一个类型参数
- template <class Tl, class T2>
- T2 print(T1 argl, T2 arg2)
- {
- cout << arg1 << " " << arg2 << endl;
- return arg2;
- }
使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员,某些成员函数的参数,返回值或局部变量能取任意类型
类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,因此类模板是属于更高层次的抽象。由于类模板需要一种或多种类型参数,所以类模板也常常称为参数化类
- template <typename T>
- class Complex{
- public:
- //构造函数
- Complex(T a, T b)
- {
- this->a = a;
- this->b = b;
- }
-
- //运算符重载
- Complex<T> operator+(Complex &c) \\Complex<T>是具化了返回类型
- {
- Complex<T> tmp(this->a+c.a, this->b+c.b);
- return tmp;
- }
-
- private:
- T a;
- T b;
- }
-
- int main()
- {
- //对象的定义,必须声明模板类型,因为要分配内容
- Complex<int> a(10,20);
- Complex<int> b(20,30);
- Complex<int> c = a + b;
-
- return 0;
- }
- template <typename T>
- class Blob
- {
- public:
- Blob();
- Blob(std::initializer_list<T> i);
- T func(T const &str);//在类内声明
- };
-
- //类外定义
- template <typename T>
- T Blob<T>::func(T const &str) \\这里可以好好看看
- {
-
- }
建立对象:
有点像vector<类型名>
类模板还有很多知识,但是书上没有了,考试应该也不会涉及
线性群体中的元素按位置排列有序
- #ifndef ARRAY_H
- #define ARRAY_H
- #include <cassert>
-
- template<class T>
- class Array {
- private:
- T* list; //T类型指针,用于存放动态分配的数组内存首地址
- int size; //数组大小(元素个数)
- public:
- Array(int sz = 50); //构造函数
- Array(const Array<T>& a); //复制构造函数
- ~Array(); //析构函数
- Array<T>& operator=(const Array<T>& rhs); //重载"="使数组对象可以整体赋值
- T& operator[](int i); //重载"[]",使Array对象可以起到C++普通数组的作用
- const T& operator [] (int i) const; //"[]"运算符针对const的重载
- operator T* (); //重载到T*类型的转换,使Array对象可以起到C++普通数组的作用
- operator const T* () const; //到T*类型转换操作符针对const的重载
- int getSize() const; //取数组的大小
- void resize(int sz); //修改数组的大小
- };
-
- //构造函数
- template<class T>
- Array<T>::Array(int sz) {
- assert(sz >= 0);
- size = sz;
- list = new T[size];
- }
-
- //析构函数
- template<class T>
- Array<T>::~Array() {
- delete[] list;
- }
-
- //复制构造函数
- template<class T>
- Array<T>::Array(const Array<T>& a) {
- size = a.size;
- list = new T[size];
- for (int i = 0; i < size; i++)
- list[i] = a.list[i];
- }
-
- //重载"="运算符,将对象rhs赋值给本对象,实现对象之间的整体赋值
- template<class T>
- Array<T>& Array<T>::operator=(const Array<T>& rhs) {
- if (&rhs != this) {
- if (size != rhs.size) {
- delete[] list;
- size = rhs.size;
- list = new T[size];
- }
- for (int i = 0; i < size; i++)
- list[i] = rhs.list[i];
- }
- return *this; //因为返回的是引用类型,所以这里加了*,返回的引用会自动绑定
- }
-
- //重载下标运算符,实现与普通数组一样通过下标访问元素,并且具有越界检查功能
- template<class T>
- T& Array<T>::operator[] (int n) {
- assert(n >= 0 && n < size);
- return list[n];
- }
-
- template<class T>
- const T& Array<T>::operator[] (int n) const {
- assert(n >= 0 && n < size);
- return list[n];
- }
-
- //重载指针转换运算符,将Array类的对象名转换为T类型的指针
- 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 size;
- }
-
- //将数组大小修改为sz
- template<class T>
- void Array<T>::resize(int sz) {
- assert(sz >= 0);
- if (sz == size)
- return;
- T* newList = new T[sz];
- int n = (sz < size) ? sz : size;
- for (int i = 0; i < n; i++)
- newList[i] = list[i];
- delete[] list;
- list = newList;
- size = sz; //这里不用delete[] newList,因为这一段内存空间已经给予了list了
- }
- #endif
注意看一下 =,T* 的运算符重载,返回类型是引用(原因后面有说)
如果类模板中涉及到地址(或是如果类中成员函数有指针)则需要设计复制构造函数实现深复制(浅复制在之前已经说过——共用一段内存空间,在执行析构函数时会将两个对象都删掉)
返回值类型为对象的引用
简而言之——当这个运算符在我们一般写法是放在左边的,那么他的重载函数返回类型就得是引用(指针符例外)
在上面的那段长代码中表现的就是operator T*
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。