c/c++零基础坐牢第十八天
c/c++从入门到入土(18)
开始时间:2023-06-06 17:55:19
结束时间:2023-06-07 01:57:50
前言:最近的两次测验一共94分,所说带有点小技巧但结果是好的,接下来学习第九章的知识 模板与群体数据。课本知识参考于清华大学出版社郑莉老师的《C++语言程序设计》第五版,以下为自制思维导图:
第九章 模板与群体数据

1 群体可分为两类:线性群体和非线性群体。 2 线性群体中的元素按位置排序有序。 3 非线性群体不用位置顺序来标识元素。 4 在排序过程中需要完成两种基本操作:一是比较两个元素的大小;二是调整元素在序列中的位置。

1 C艹最重要的特性之一就是代码重用,为了实现代码重用必须具有通用性。同样代码需要不受数据类型的影响,并且可以自动适应数据类型的变化,这种程序设计类型称为参数化程序设计。 2 模板是C++支持参数化程序设计的工具,通过它可以实现参数化多态性。 3 所谓参数化多态性,就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。 4 函数模板适用于多种不同数据类型,使代码的可重用性大大提高,从而提高软件的开发效率,程序员只需对函数模板编写一次,然后基于调用函数时提供的参数类型,C++编译器将自动产生相应的函数来正确的处理该类型的数据。 5 函数模板的定义形式是: 6 template<模板参数表> 7 类型名 函数名(参数表) 8 { 9 函数体的定义 10 } 11 所有函数模板的定义都是用关键字template开始的,该关键字之后是使用尖括号<>括起来的“模板参数表”。 12 模板参数表由用逗号分隔的模板参数构成,可以包括以下内容: 13 (1)class(或typename)标识符,指明可以接受一个类型参数。这些类型参数代表的是类型,可以是预定义类型或自定义类型。 14 (2)类型说明符 标识符,指明可以接受一个由“类型说明符”所规定类型的常量作为参数; 15 (3)template<参数表>class标识符,指明可以接收一个类模板名作为参数。 16 类型参数可以用来指定函数模板本身的形参类型、返回值类型,以及声明函数中的局部变量。 17 当类型参数的含义确定后,编译器将以函数模板为样板,生成一个函数,这一过程称为函数模板的实例化,该函数称为函数模板的一个实例。 18 虽然函数模板的使用形式与函数类似,但二者有本质的区别,这主要表现在以下三个方面: 19 (1)函数模板本身在编译时不会生成任何目标代码,只有由模板生成的实例会生成目标代码。 20 (2)被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。 21 (3)函数指针也只能指向模板的实例,而不能指向模板本身。

1 使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、返回值或局部变量能取不同类型(包括系统预定义的和用户自定义的)。 2 类是对一组对象的公共性质的抽象。由于类模板需要一种或多种类型参数,使用类模板也常常称为参数化类。 3 类模板声明的语法形式是: 4 template<模板参数表> 5 class 类名 6 { 7 类成员声明 8 } 9 其中类成员声明的方法与普通类的定义几乎相同,只是在它的各个成员(数据成员和函数成员)中通常要用到模板的类型参数T。其中“模板参数表”的形式与函数模板中的“模板参数表”完全一样。 10 如果需要在类模板以外的地方定义其成员函数,则要采用以下的形式: 11 template<模板参数表> 12 类型名 类名<模板参数标识符列表>::函数名(参数表) 13 一个类模板声明自身并不是一个类,只有当被其他代码引用时,模板才根据引用的需要生成具体的类。 14 使用一个模板类来建立对象时,应按如下形式声明: 15 模板名<模板参数表> 对象名1,...,对象名n;

1 线性群体中的元素次序与其位置关系是对应的。在线性群体中,又可按照访问元素的不同方法分为直接访问、顺序访问和索引访问。 2 对可直接访问的线性群体,可以直接访问群体中的任何一个元素,而不比首先访问该元素之前的元素。 3 对顺序访问的线性群体,只能按元素的排列顺序从头开始依次访问各个元素。 4 栈是只能从一段访问的线性群体,可以访问的这一段称为栈顶,另一端称为栈底。对栈顶位置的标记称为栈顶指针,对栈底位置的标记称为栈底指针。向栈顶添加元素称为“压入栈”,删除栈顶元素称为“弹出栈”。栈中元素的添加和删除操作具有“后进先出”(LIFO)的特性。 5 栈的应用非常广泛,编译系统就是利用栈来实现函数调用时的参数传递和保留返回地址。编译器对高级语言中表达式的处理也可以通过栈来实现。 6 队列是只能向一端添加元素,从一端删除元素的线性群体,可以添加元素的一端称为队尾,可以删除元素的一端称为队头。对队头位置的标记称为队头指针,对队尾位置的标记称为队尾指针。向队尾添加元素称为“入队”,删除队头元素称为“出队”。 7 动态数组类模板Array,它由任意多个位置连续的、类型相同的元素组成,其元素个数可在程序运行时改变,它虽然比vector简单,但与vector的工作原理类似。类Array弥补了数组的不足,其大小可变,且具有边界检查功能,可以捕捉非法的数组下标。由于对下标运算符“[]”和指针转换运算符“T *”进行了重载,使得Array类的对象可以像普通数组一样使用。因此我们可以用类Array来代替C++语言本身的数组,充分利用其安全性和可调性。 8 如果将复制构造函数改为如下形式就是浅层复制。 9 template<class T> 10 Array<T>::Array(const Array<T>& a){ 11 } 12 链表是一种动态数据结构,可以用来表示顺序访问的线性群体。链表是由一系列节点组成的,结点可以在运行时动态生成。每个结点包括数据域和指向链表中下一个结点的指针(即下一个结点的地址)。 13 链表的结点包括数据和指针域,是链表的基本构件。结点的数据域用于存放群体中元素的内容,既可以是若干个基本类型的数据,也可以是自定义类型的数据,甚至是内嵌对象。结点的指针域用于存放链表中另一个结点的地址。 14 结点类的数据成员中应该包括数据域和指针域的内容,函数成员中应该含有对数据和指针进行初始化的方法(函数),以及在本结点之后插入新结点和删除后继节点的方法。 15 链表类表示的是一个顺序访问的线性群体,由一组用指针串联的Node模板对象构成,对链表的任何操作都有由头结点开始,因此对于所有应用链表的程序来说,链表的头指针是必然要使用的。尾指针对许多应用来说也是有用的信息。 16 在对链表的顺序访问中,娃娃需要有一个指针自头结点开始遍历各结点,最终达到需要的位置,将这一指针所指向的结点称为当前结点;另外还需要一个伴随指针,始终指向当前结点的前驱结点,以配合完成结点的插入、删除等操作。 17 在链表类的数据成员中,需要保存表头指针、表尾指针、元素个数、当前的遍历位置等信息。 18 链表的基本操作应该包括:生成新结点、插入结点、删除结点。访问/修改结点数据、遍历链表等。因此,在链表类中应该包含完成上述操作的成员函数,以及为了实现这些函数而添加的一些辅助函数,为了方便链表类对象间的赋值,还应重载“=”运算符。另外,由于面向对象的封装特性,当然还要提供一些接口函数。 19 由于栈也是一种线性群体,因此栈的数据可以用数组或链表来存储。 20 栈的基本状态有:一般状态、栈空、栈满。当栈中没有元素时称为栈空;当栈中元素个数达到上限时,称为栈满;栈中有元素、但未达到栈满状态,即处于一般状态。 21 队列也有3种基本状态:一般状态、队空、队满。当队中没有元素时称为队空;当队中元素个数达到上限时,称为队满;队中有元素,但未达到队满状态时,即处于一般状态。 22 无论采用哪种数据结构,队列类的数据成员都应包括:队列元素、队头指针和队尾指针。队列类中函数成员应该能够实现下列基本操作:初始化、入队、出队、清空队列、访问队首元素、检测队列的状态(满、空、队列长度)。

1 插入类排序的基本思想是:每一步将一个待排列元素按其关键字值的大小插入到已排序的适当位置上,直到待排序元素插入完为止。如果要对具有n个元素的数组a进行排序,初始状态时,可以认为已排序序列为a[0],待排序序列为a[1]~a[n-1]。在插入排序过程中,由于寻找插入位置方法不同又可以分为不同的插入排序。 2 选择排序的基本思想是:每次从待排序序列中选择一个关键字最小的元素,(当需要按关键字升序排列时),顺序排在序列的最后,直至全部排完。在选择类排序方法中,根据从待排序序列中选择元素的方法不同,又分为不同的选择排序方法,其中最简单的是通过顺序比较找出待排序序列中最小元素,称为简单选择排序。
实验四 模板编程

template <typename T> T Double (T x) { return 2*x; }
分析:该题需要我们做到利用函数模板解决双倍功能,众所周知,利用函数模板编译器自己会根据参表给出对应的函数,需要记住函数模板的定义形式template<模板参数表>其他和正常函数一致。

6-2 【CPP0037】利用类模板解决绝对值功能 分数 10 作者 C++模板编程 单位 石家庄铁道大学 请使用模板参数设计实现绝对值模板类Absolute,Absolute类功能要求成员函数getValue(void)const计算类数据的绝对值,类数据类型应能适应整型、浮点型、双精度型等各种类型,绝对值类型与类数据一样。 裁判测试程序样例: #include <iostream> using namespace std; /*请在这里填写答案*/ int main(void){ char c='\0'; int i=0; long l=0; scanf("%c%d%ld",&c,&i,&l); float f=1.1; double d=2.2; scanf("%f%lf",&f,&d); Absolute<char> dc(c); cout<<dc.getValue()<<endl; Absolute<int> di(i); cout<<di.getValue()<<endl; Absolute<long> dl(l); cout<<dl.getValue()<<endl; Absolute<float> df(f); cout<<df.getValue()<<endl; Absolute<double> dd(d); cout<<dd.getValue()<<endl; return 0; } 输入样例: a -2 300000 -4.56 7.89 输出样例: a 2 300000 4.56 7.89 代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB
template <class T> class Absolute { private: T x; public: Absolute(T x1) { x=x1; } T getValue(void)const; }; template<class T> T Absolute<T>::getValue(void) const { return x<0 ? -x : x; }

请使用模板参数设计实现单向链表模板类LinkList,应能根据需求构建相应类型数据结点的单向链表结构,main(void)完成对其的测试。 设计要求: (1)设计实现结点模板类Node,结点的数据域应能各种类型数据;其中成员函数getData(void)的作用是获取结点的数据域。构造函数输出信息“Node Constructor run”,拷贝构造函数输出信息“Node CopyConstructor run”,析构函数输出信息“Node Destructor run” (2)在结点模板类Node的支持下设计实现结点单向链表模板类LinkList,其中: ①LinkList类的数据包括结点链表的头结点headNode和游标position(游标用于表示当前操作位置) ②有参构造函数实现由数组构建链表的功能,构造函数输出信息“LinkList Constructor run”;拷贝构造函数输出信息“LinkList CopyConstructor run”,析构函数输出信息“LinkList Destructor run” ③设计实现成员函数insertNode(n),其功能为在游标位置后插入一个同类型结点n。 ④设计实现成员函数searchNode(value),其功能为在链表中查找数据域等于value的结点,若查找成功的同时修改游标位置。 ⑤设计实现成员函数getSize(),其功能为返回链表中的元素个数。 ⑥设计实现成员函数next(),其功能为使游标移动到下一个结点。 ⑦设计实现成员函数currNode()const,其功能为返回当前结点。 ⑧设计实现成员函数delNode(),其功能为移除当前结点。 ⑨设计实现成员函数show(),其功能为输出链表。 提示: 1 g++下定义模板类时,成员函数名不要带模板参数; 2 delete只能删除new产生的结点 裁判测试程序样例: #include <iostream> using namespace std; /*请在这里填写答案*/ int main() { int i,a[5]= {0,1,2,3,4}; for(i=0;i<5;i++) scanf("%d",&a[i]); LinkList<int> l1(a,5),l2(l1); cout<<l2.getSize()<<endl; l1.show(); if (l2.searchNode(2)) cout<<"Found:"<<l2.currNode().getData()<<endl; else cout<<"Not Found"<<endl; l2.delNode(); Node <int> *p1=new Node<int>(11); l2.insertNode(*p1); l2.show(); return 0; } 输入样例: 1 2 3 4 5 输出样例: Node Constructor run Node Constructor run Node Constructor run Node Constructor run Node Constructor run Node Constructor run LinkList Constructor run Node Constructor run Node Constructor run Node Constructor run Node Constructor run Node Constructor run Node Constructor run LinkList CopyConstructor run 5 [1][2][3][4][5] Found:2 Node Constructor run [1][11][3][4][5] Node Destructor run Node Destructor run Node Destructor run Node Destructor run Node Destructor run LinkList Destructor run Node Destructor run Node Destructor run Node Destructor run Node Destructor run Node Destructor run Node Destructor run LinkList Destructor run Node Destructor run 代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB
template<typename T> class Node { public: T data; Node<T>* next; Node<T>* last; Node(T d=0) { data=d; cout<<"Node Constructor run"<<endl; } Node(Node<T> &n) { data=n.data; next=n.next; cout<<"Node CopyConstructor run"<<endl; } ~Node() { cout<<"Node Destructor run"<<endl; } T getData() { return data; } }; template<typename T> class LinkList { private: Node<T> headNode; //头结点 Node<T>* position; //游标 int S; public: LinkList(T *x,int size) { int i; Node<T>* p; p=&headNode; headNode.last=NULL; for(i=0;i<size;i++) { position=new Node<T>(x[i]); position->last=p; p->next=position; p=position; } p->next=NULL; S=size; cout<<"LinkList Constructor run"<<endl; } LinkList(LinkList<T> &y) { int i; S=y.S; Node<T>* p; Node<T>* yy=y.headNode.next; p=&headNode; headNode.last=NULL; for(i=0;i<S;i++) { position=new Node<T>(yy->getData()); p->next=position; position->last=p; p=position; yy=yy->next; } p->next=NULL; position=headNode.next; cout<<"LinkList CopyConstructor run"<<endl; } ~LinkList() { Node<T>* p; p=headNode.next; for(int i=0;i<S;i++) { position=p; p=position->next; delete position; } cout<<"LinkList Destructor run"<<endl; } void insertNode(Node<T> &n) //在游标位置后插入一个同类型结点n { n.next=position->next; position->next=&n; S++; } int searchNode(T value) //在链表中查找数据域等于value的结点,若查找成功的同时修改游标位置。 { int i; Node<T>* p=headNode.next; for(i=0;i<S;i++) { if(p->getData()==value) {position=p;return 1;} p=p->next; } return 0; } void next()//使游标移动到下一个结点 { if(position->next!=NULL) position=position->next; } Node<T>& currNode()const //返回当前结点。 { return *position; } void delNode() //移除当前结点 { Node<T> *t=position; if(position->next==NULL) { position=position->last; position->next=NULL; } else { t->last->next=t->next; t->next->last=t->last; position=position->last; } S--; } void show() //输出链表。 { int i; Node<T> *p=headNode.next; for(i=0;i<S;i++){ cout<<"["<<p->getData()<<"]"; p=p->next; } cout<<endl; } int getSize() //返回链表中的元素个数 { return S; } };

应用STL中的vector完成功能测试。 设计要求: 定义一个空的vector,将用户输入的数组a[10]的10个数插入到vector中,在vector头部插入数b,用迭代器遍历vector并输出其中的元素值。然后将vector从小到大排序,删除vector尾部的元素,用迭代器遍历vector并输出其中的元素值。最后将vector清空。 裁判测试程序样例: #include<iostream> #include<vector> #include<algorithm> using namespace std; int main(void) { int i,a[10],b; for(i=0; i<10; i++){ scanf("%d",&a[i]); } scanf("%d",&b);//插入的数 { /*请在这里填写答案*/ } return 0; } 输入样例: 9 8 7 6 5 4 3 2 1 10 0 输出样例: [0][9][8][7][6][5][4][3][2][1][10] [0][1][2][3][4][5][6][7][8][9] 代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB
vector<int>v; v.push_back(b); for( i=0;i<10;i++) { v.push_back(a[i]); } for(vector<int>::iterator it=v.begin();it!=v.end();it++) { cout<<"["<<*it<<"]"; } cout<<endl; sort(v.begin(),v.end()); v.pop_back(); for(vector<int>::iterator it=v.begin();it!=v.end();it++) { cout<<"["<<*it<<"]"; }

应用STL中的list完成功能测试。 设计要求: 定义一个空的list,将用户输入的数组a[10]的10个数插入到list中,在list头部插入数b,用迭代器遍历list并输出其中的元素值。然后将list从大到小排序,删除list尾部的元素,用迭代器遍历list并输出其中的元素值。最后将list清空。 裁判测试程序样例: #include<iostream> #include<list> #include<algorithm> using namespace std; int main(){ int i,a[10],b; for(i=0; i<10; i++){ scanf("%d",&a[i]); } scanf("%d",&b);//插入的数 { /*请在这里填写答案*/ } return 0; } 输入样例: 10 1 2 3 4 5 6 7 8 9 0 输出样例: [0][10][1][2][3][4][5][6][7][8][9] [10][9][8][7][6][5][4][3][2][1] 代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB
list<int> l1; list<int> l2; l1.push_back(b); for(i=0;i<10;i++) { l1.push_back(a[i]); } for(list<int>::iterator it=l1.begin();it!=l1.end();it++) { cout<<"["<<*it<<"]"; } cout<<endl; int c[11]; for(int i=0;i<10;i++) { c[i]=a[i]; } c[10]=b; int temp; for(int i=0;i<10;i++) { for(int j=i+1;j<=10;j++) { if(c[i]<c[j]) { temp=c[i]; c[i]=c[j]; c[j]=temp; } } } for(int i=0;i<=10;i++) { l2.push_back(c[i]); } l2.pop_back(); for(list<int>::iterator it=l2.begin();it!=l2.end();it++) { cout<<"["<<*it<<"]"; }
总结:在C++中,用函数图像代表一串点,我们表示相同特征的点时只需要给出一个函数表达式;用集合的形式表示一组数据的特征,我们便不需要一个个将数据枚举出来表示,而用函数模板,我们便不需要一个个重载函数来完成一个个相同的函数功能。
每日一mo:好久没见了,是高考将我们分开的么?也许不是。高中三年好短啊,曾以为很漫长,没想到说过去就过去了。你越来越成熟了,而我好像一直没长大,一直这样呆呆的,傻傻的,挺好笑的对吧。没想到吧,上次回高中,我真的做到老师提前你的名字我神色不变,老师讲起你的大学生活转过头看我回了句“不熟”,如果是你,你会怎么回答呢?我好像不在乎。我这一年过得其实还不错,虽然离开了湖北,虽然并没有发挥好来了一所普本,虽然真的真的过的很艰难,但我也真的真的遇到了形形色色的人,交到了新的朋友,享受着不一样的大学生活,还记得你说春天要每天吃一个苹果,夏天记得多运动,秋天不要撕嘴皮(不知道某人有没有改掉这个从我这里学来的坏毛病),冬天多穿衣服,北方的冬天很冷冷得很真实,河北的春天春如四季呢,夏天也没有武汉那么那么热,说到热我又想起了那家中百超市,哈哈哈哈。你过得还好么?或许你永远不会再回应我了,哪怕你再甩这臭脸把我骂一顿,哪怕你再发一串又一串让我想哭的短信,还别说,好像我们当初的女同学上了大学都找到男朋友了,99的祝福好像还差对你说一次,想必他一定是特别特别优秀、有责任心、有孝心、不会反感你追星、身体素质特别好的男孩吧,平平安安,少耍点小脾气,不仅是对身边的朋友对每个人都要平和一些,当然也要多运动,一个近视一个远视的眼睛要多注意休息。六月七日又是一年高考了,劫随风起平云渡,祸福调和安喜乐,说是一年其实是四年啊......
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程