C++——模板、数组类
1、函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计。
声明方法:template<typename 标识符> 函数声明
求绝对值的模板
#include<iostream> ……编译器从调用abs函数时实参的类型,推导出函数模板的类型参数。
using namespace std; ……如该题从调用abs(int)推导出模板中类型参数T为int型。
template<typename T> ……当类型参数的含义确定后,编译器将函数模板为样板,生成一个函数:
T abs(T x) …… int abs(int x)
{ return x<0?-x:x; } ……{return x<0?-x:x;}
int main()
{ int n=-5;
double d=-5.5;
cout<<abs(n)<<endl;
cout<<abs(d)<<endl;
}//运行结果:5 5.5
2、类模板:使用类模板用户可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值,能取任意类型(包括基本类型和自定义类型)。
类模板声明:template <模板参数表> class 类名 {类成员声明}
如果需要在类模板之外定义其成员函数,则要采用template <模板参数表> 类型名 类名<T>::函数名(参数表)
#include <iostream>
#include <cstdlib>
using namespace std;
struct Student // 结构体Student
{
int id; //学号
float gpa; //平均分
};
template <class T> //类模板:实现对任意类型数据进行存取
class Store
{
private:
T item; // item用于存放任意类型的数据
int haveValue; // haveValue标记item是否已被存入内容
public:
Store(void); // 缺省形式(无形参)的构造函数
T GetElem(void); //提取数据函数
void PutElem(T x); //存入数据函数
};
//以下实现各成员函数。
//注意:模板类的成员函数,若在类外实现,则必须是模板函数
template <class T> // 缺省形式构造函数的实现
Store<T>::Store(void): haveValue(0)
{}
template <class T> // 提取数据函数的实现
T Store<T>::GetElem(void)
{
if (haveValue == 0) // 如果试图提取未初始化的数据,则终止程序
{
cout << "No item present!" << endl;
exit(1);
}
return item; // 返回item中存放的数据
}
template <class T> // 存入数据函数的实现
void Store<T>::PutElem(T x)
{
haveValue++; // 将haveValue 置为 TRUE,表示item中已存入数值
item = x; // 将x值存入item
}
int main()
{
Student g= {1000, 23}; //声明Student类型结构体变量的同时赋以初值
Store<int> S1, S2; //声明两个Store<int>类对象,其中数据成员item为int类型
Store<Student> S3;//声明Store<Student>类对象S3,其中数据成员item为Student类型
Store<double> D; //声明Store<double>类对象D,其中数据成员item为double类型
S1.PutElem(3); //向对象S1中存入数据(初始化对象S1)
S2.PutElem(-7); //向对象S2中存入数据(初始化对象S2)
cout<<S1.GetElem()<<" "<<S2.GetElem()<<endl; //输出对象S1和S2的数据成员
S3.PutElem(g); //向对象D中存入数据(初始化对象D)
cout <<"The student id is "<<S3.GetElem().id << endl; //输出对象S3的数据成员
cout << "Retrieving object D " ;
cout << D.GetElem() << endl; //输出对象D的数据成员
// 由于D未经初始化,在执行函数D.GetElement()过程中导致程序终止
}
3、数组类--属于直接访问群体
群体的概念:有多个数据元素组成的集合体。群体可以分为两大类:线性群体和非线性群体。线性群体中的元素按位置排列有序,可以区分为第一个元素、第二个元素等。其元素与其位置的关系是对应的,在线性群体中按照访问方法不同,我们可以将其分为直接访问、顺序访问和索引访问。非线性群体不用位置顺序来标识元素。
静态数组:是具有固定元素个数的群体,其中元素可以通过下标访问,其大小在编译时就已经确定,在运行过程中无法修改;
动态数组:有一系列位置连续的任意数量的相同类型的元素组成,其元素个数在程序运行时改变。
动态数组类的模板:Array类模板
//Array.h
#ifndef ARRAY_CLASS
#define ARRAY_CLASS
#include <iostream>
#include <cstdlib>
using namespace std;
#ifndef NULL
const int NULL = 0;
#endif // NULL
//错误类型集合,共有三种类型的错误:数组大小错误、内存分配错误和下标越界
enum ErrorType
{invalidArraySize, memoryAllocationError, indexOutOfRange};
//错误信息
char *errorMsg[] =
{
"Invalid array size", "Memory allocation error",
"Invalid index: "
};
//数组类模板声明
template <class T>
class Array
{
private:
T* alist; //T类型指针,用于存放动态分配的数组内存首地址
int size; //数组大小(元素个数)
void Error(ErrorType error,int badIndex=0) const; // 错误处理函数
public:
Array(int sz = 50); //构造函数
Array(const Array<T>& A); //拷贝构造函数
~Array(void); //析构函数
Array<T>& operator= (const Array<T>& rhs); //重载"="使数组对象可以整体赋值
T& operator[](int i); //重载"[]",使Array对象可以起到C++普通数组的作用
operator T* (void) const; //重载T*,使Array对象可以起到C++普通数组的作用
int ListSize(void) const; // 取数组的大小
void Resize(int sz); // 修改数组的大小
};
//以下为类成员函数的定义
//模扳函数Error实现输出错误信息的功能
template <class T>
void Array<T>::Error(ErrorType error, int badIndex) const
{
cout << errorMsg[error]; //根据错误类型,输出相应的错误信息
if (error == indexOutOfRange)
cout << badIndex; //如果是下标越界错,输出错误的下标
cout << endl;
exit(1);
}
//构造函数
template <class T>
Array<T>::Array(int sz)
{
if (sz <= 0) //sz为数组大小(元素个数),若小于0,则输出错误信息
Error(invalidArraySize);
size = sz; // 将元素个数赋值给变量size
alist = new T[size]; //动态分配size个T类型的元素空间
if (alist == NULL) //如果分配内存不成功,输出错误信息
Error(memoryAllocationError);
}
// 析构函数
template <class T>
Array<T>::~Array(void)
{ delete [] alist; }
// 拷贝构造函数
template <class T>
Array<T>::Array(const Array<T>& X)
{
//从对象X取得数组大小,并赋值给当前对象的成员
int n = X.size;
size = n;
//为对象申请内存并进行出错检查
alist = new T[n]; // 动态分配n个T类型的元素空间
if (alist == NULL) //如果分配内存不成功,输出错误信息
Error(memoryAllocationError);
// 从对象X复制数组元素到本对象
T* srcptr = X.alist; // X.alist是对象X的数组首地址
T* destptr = alist; // alist是本对象中的数组首地址
while (n--) // 逐个复制数组元素
*destptr++ = *srcptr++;
}
// 重载"="运算符,将对象rhs赋值给本对象。实现对象之间的整体赋值
template <class T>
Array<T>& Array<T>::operator= (const Array<T>& rhs)
{
int n = rhs.size; // 取rhs的数组大小
//如果本对象中数组大小与rhs不同,则删除数组原有内存,然后重新分配
if (size != n)
{
delete [] alist; // 删除数组原有内存
alist = new T[n]; // 重新分配n个元素的内存
if (alist == NULL) //如果分配内存不成功,输出错误信息
Error(memoryAllocationError);
size = n; //记录本对象的数组大小
}
// 从rhs向本对象复制元素
T* destptr = alist;
T* srcptr = rhs.alist;
while (n--)
*destptr++ = *srcptr++;
return *this; // 返回当前对象的引用
}
// 重载下标运算符,实现与普通数组一样通过下标访问元素,并且具有越界检查功能
template <class T>
T& Array<T>::operator[] (int n)
{
if (n < 0 || n > size-1) // 检查下标是否越界
Error(indexOutOfRange,n);
return alist[n]; // 返回下标为n的数组元素
}
//重载指针转换运算符,将Array类的对象名转换为T类型的指针,
//指向当前对象中的私有数组。
//因而可以象使用普通数组首地址一样使用Array类的对象名
template <class T>
Array<T>::operator T* (void) const
{
return alist; // 返回当前对象中私有数组的首地址
}
//取当前数组的大小
template <class T>
int Array<T>::ListSize(void) const
{
return size;
}
// 将数组大小修改为sz
template <class T>
void Array<T>::Resize(int sz)
{
if (sz <= 0) // 检查是否sz<= 0
Error(invalidArraySize);
if (sz == size) // 如果指定的大小与原有大小一样,什么也不做
return;
T* newlist = new T[sz]; // 申请新的数组内存
if (newlist == NULL) // 测试申请内存是否申请成功
Error(memoryAllocationError);
int n = (sz <= size) ? sz : size; // 将sz与size中较小的一个赋值给n
// 将原有数组中前n个元素复制到新数组中
T* srcptr = alist; // 原数组alist的首地址
T* destptr = newlist; // 新数组newlist的首地址
while (n--) // 复制数组元素
*destptr++ = *srcptr++;
delete[] alist; // 删除原数组
alist = newlist; // 使alist 指向新数组
size = sz; // 更新size
}
#endif // ARRAY_CLASS
Array类模板的应用:求范围2~N中的质数,N在程序运行时由键盘输入 。
#include <iostream>
#include <iomanip>
#include "Array.h"
using namespace std;
int main()
{ Array<int> A(10);
int n;
int primecount = 0, i, j;
cout << "Enter a value >= 2 as upper limit for prime numbers: ";
cin >> n;
A[primecount++] = 2; // 2是一个质数,[]下标运算
for(i = 3; i < n; i++)
{ if (primecount == A.ListSize()) A.Resize(primecount + 10);
if (i % 2 == 0) continue;
j = 3;
while (j <= i/2 && i % j != 0) j += 2;
if (j > i/2) A[primecount++] = i;
}
for (i = 0; i < primecount; i++)
{ cout << setw(5) << A[i];
if ((i+1) % 10 == 0) cout << endl;
}
cout << endl;
}
链表、栈和队列等属于数据结构中已经详细说明的内容,在此省略。
4、几种简单的内部排序的算法(插入、选择和交换)
排序的过程主要完成的动作有两个:比较大小和调整元素在序列中的位置。关于数据元素,在排序过程中需要一个标识符,也就是排序关键值,可以标识一个数据元素,也就是数据元素中某个数据项的值。
插入排序:每一步将一个待定排序元素按期关键字值的大小插入到已排序列的适当位置上,知道待排元素插入完毕止。直接插入排序函数模板
template <class T>
void InsertionSort(T A[], int n)
{ int i, j;
T temp;
for (i = 1; i < n; i++)
{ j = i;
temp = A[i];
while (j > 0 && temp < A[j-1])
{ A[j] = A[j-1];
j--;
}
A[j] = temp;
}
}
选择排序:每次排序从待排序的序列中选择一个关键字最小或者最大的元素,顺序排在已排序列的最后,直至全部完成。直接选择的函数模板
template <class T>
void Swap (T &x, T &y)
{ T temp;
temp = x;
x = y;
y = temp;
}
template <class T>
void SelectionSort(T A[], int n)
{ int smallIndex;
int i, j;
for (i = 0; i < n-1; i++)
{ smallIndex = i;
for (j = i+1; j < n; j++)
if (A[j] < A[smallIndex]) smallIndex = j;
Swap(A[i], A[smallIndex]);
}
}
交换排序:两两比较待排序序列中的元素,并交换不满足顺序要求的各对元素,知道全部满足顺序要求为止。学习了最简单的起泡排序。对具有n个元素的序列按升序进行起泡排序的步骤:
首先将第一个元素与第二个元素进行比较,若为逆序,则将两元素交换。然后比较第二、第三个元素,依次类推,直到第n-1和第n个元素进行了比较和交换。此过程称为第一趟起泡排序。经过第一趟,最大的元素便被交换到第n个位置。
对前n-1个元素进行第二趟起泡排序,将其中最大元素交换到第n-1个位置。
如此继续,直到某一趟排序未发生任何交换时,排序完毕。对n个元素的序列,起泡排序最多需要进行n-1趟。
起泡法的函数模板
template <class T>
void Swap (T &x, T &y)
{ T temp;
temp = x;
x = y;
y = temp;
}
template <class T>
void BubbleSort(T A[],int n)
{int i,j;
int lastExchangeIndex;
i = n-1;
while (i > 0)
{ lastExchangeIndex = 0;
for (j = 0; j < i; j++)
if (A[j+1] < A[j])
{ Swap(A[j],A[j+1]);
lastExchangeIndex=j;
}
i=lastExchangeIndex;
}
}
C++——模板、数组类的更多相关文章
- C++_进阶之函数模板_类模板
C++_进阶之函数模板_类模板 第一部分 前言 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来 ...
- C++数组类模板
* 作为数组类模板,肯定没有vector做得好,可是普通的数组有1个优点就是能直接操作内存.vector在这方面就不是非常方便了. 网上尽管也有数组类模板.多维的设计基本上都不是非常好.我这个类模板多 ...
- 网易云课堂_C++程序设计入门(下)_第10单元:月映千江未减明 – 模板_第10单元 - 单元作业:OJ编程 - 创建数组类模板
第10单元 - 单元作业:OJ编程 - 创建数组类模板 查看帮助 返回 温馨提示: 1.本次作业属于Online Judge题目,提交后由系统即时判分. 2.学生可以在作业截止时间之前不限次数提 ...
- C++走向远洋——64(项目三、数组类模板)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- PHP模板解析类实例
作者:mckee 这篇文章主要介绍了PHP模板解析类,涉及php针对模板文件的解析与字符串处理的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下 <?php class template { ...
- 2014金山笔试_编写一个数组类 MyVector
//编写一个数组类 MyVector,数组内容可以动态扩充,实现构造,析构,赋值操作符重载,插入,删除,获取元素个数,获取数组容量(不可以使用STL等的容器类,不能使用 //不连续的存储空间) #in ...
- 1.3eigen中数组类和系数的运算
1.3数组类和系数的运算 与矩阵类只适用与线性代数运算相反,数组类提供通用的数组类,能不利用线性代数的知识来对系数进行操作,比如对每个系数加上一个常数,或者乘上两个数组的系数. 1.数组类型 跟矩阵类 ...
- C++复习:函数模板和类模板
前言 C++提供了函数模板(function template).所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表.这个通用函数就称为函数模板.凡是函数体 ...
- C++解析(26):函数模板与类模板
0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1. ...
随机推荐
- mysql添加索引(建表之后)
一.使用ALTER TABLE语句创建索引 语法如下: alter table table_name add index index_name (column_list) ; alter table ...
- js中(function(){})()的写法用处
直到今天我才明白的一个玩意!!! 来来来,首先嘛,JS中函数有两种命名方式 1.一种是声明式. 而声明式会导致函数提升,function会被解释器优先编译.即我们用声明式写函数,可以在任何区域声明,不 ...
- 【已解决2】pyinstaller UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xce in position 110: invalid continuation byte
python打包exe解码错误问题 最近做了一个小项目,其中把自己写的python打包成exe文件.我用的是pyinstaller. 只需要打包主程序py文件就ok. 在打包过程中,遇到一 ...
- Markdown语法,及其在typora中的快捷键,学写博客吧!!!
前言 Markdown (MD) 是现在最流行的一种文档书写语言格式.平常写笔记,写博客,写计划再好不过了.个人觉得使用很简单,右击鼠标,有你想要的操作. Typora是简洁.操作简单.功能强大.方便 ...
- H5网页布局+css代码美化
HTML5的结构化标签,对搜索引擎更友好 li 标签对不利于搜索引擎的收录,尽量少用 banner图片一般拥有版权,不需要搜索引擎收录,因此可以使用ul + li <samp></s ...
- 【EasyUI总结】EasyUI开发中遇到的坑
普遍: 1.easyui在书写键值对的时候要注意是否要加引号,在需要加引号的地方不加则无法渲染: datagrid数据网格: 1.datagrid默认请求方式是post,如果要使用分页功能pagina ...
- honeywell1900扫描枪的使用说明
霍尼韦尔1900扫描枪驱动是honeywell1900扫描枪的USB驱动,就是扫描枪usb转com,如果你的系统是32位的,就直接运行Install_x86.bat,如果是64位的,就运行Instal ...
- cf1242B
题意简述:给出一个n个点的完全图,边权要么是1要么是0,输入只给出权值的是1的那些边,求解最小生成树的权值 解答:边很多,我们考虑使用prim算法,prim算法的过程中维护了一个dis数组,这里我们可 ...
- flutter常用插件(持续更新)
flutter插件官网地址:https://pub.dartlang.org/packages/ 1. image_picker 一个可以从图库选择图片,并可以用相机拍摄新照片的flutter插件 2 ...
- qt 带箭头的直线 (类似viso)
2020.02.27 本来上传到CSDN,后来想想,我要放弃csdn了.csdn已经跟我分享的精神背道而驰了.想要代码,留邮箱吧. 近来Qt开发时可能遇到这样的需求:两个(或多个)矩形,要用直线将它们 ...