200 likes | 309 Views
实验二 模板的运用. 第二章预备内容的验证 完成时间: 9 月 16 日 ——9 月 19 日 提交实验报告,文件名为学号与姓名. 一、动态内存分配程序设计. 数组的定义: int a[10]; 只能说明 长度不变 的数组,而在实际应用中,我们希望数组的长度可以随时改变,或者由用户设定,这个要求可以通过“动态内存分配”技术实现。 动态内存分配:在程序运行过程中,根据需要在内存中进行内存分配,称为动态内存分配。. 动态内存分配程序设计. 程序使用的内存空间示意图: 栈区 堆区 全局数据区 程序代码区. 栈区 函数形参、局部变量 在程序 编译 时分配. 堆区
E N D
实验二 模板的运用 • 第二章预备内容的验证 • 完成时间:9月16日——9月19日 • 提交实验报告,文件名为学号与姓名
一、动态内存分配程序设计 • 数组的定义:int a[10];只能说明长度不变的数组,而在实际应用中,我们希望数组的长度可以随时改变,或者由用户设定,这个要求可以通过“动态内存分配”技术实现。 • 动态内存分配:在程序运行过程中,根据需要在内存中进行内存分配,称为动态内存分配。
动态内存分配程序设计 • 程序使用的内存空间示意图: • 栈区 • 堆区 • 全局数据区 • 程序代码区 栈区 函数形参、局部变量 在程序编译时分配 堆区 在程序运行过程中 分配的存储 全局数据区 程序代码区
动态内存分配程序设计 C++堆内存分配:运算符 new 和 delete 1.new: • 申请一个整型的空间: int *ip; ip=new int; *ip=10; • 申请一个整型空间并同时初始化: ip=new int (5); cout<<*ip<<endl; • 申请长度为10 的一维数组(数组的长度是可变的) int *ap; ap=new int[10];//注意:不能对动态数组初始化 for (int k=0;k<10;k++) cin>>ap[k]; 从堆中分配一个整型存储空间,返回指向内存的指针。 从堆中分配一个整型存储空间并初始化为5 从堆中分配连续10个整型存储空间
动态内存分配程序设计 2.Delete:把申请的堆内存空间还给系统: int *ip; ip=new int; *ip=10; delete ip; //表示归还一个整型的存储空间 int *ap; ap=new int[10]; for (int k=0;k<10;k++) cin>>ap[k]; delete[ ]ap; //表示归还一组整型的空间
动态内存分配程序设计 例1:运用动态内存分配申请一个数组,长度从外部输入: int n,*ip,*tp; cin>>n; ip=new int[n]; for (int k=0;k<n;k++) cin>>ip[k];//下标法 //指针法:ip固定 for (k=0;k<n;k++) *(ip+k)=2*(ip+k); //指针法:tp可变 for (tp=ip;tp<ip+n;tp++) *tp=2*tp; delete []ip; 思考题:这样的语句有什么问题? for (;ip<ip+n;ip++) *ip=2*ip;
动态内存分配程序设计 例2:程序可接收一个不定长的字符串: void main(){ char *cp; int len; cout<<“请输入字符串的长度:”;cin>>len; cp=new char[len+1]; cin>>cp; cout<<cp<<endl; delete []cp; }
动态内存分配程序设计 例3:程序可接收一个不定长的字符串(带输入容错) “容错”就是当用户输入不正确时,程序能对错误进行处理,确保程序不会产生严重后果。 void main(){ char *cp; int len, count=3; //最多可允许三次输入错误 do { cout<<“请输入字符串的长度(>0):”;cin>>len; } while (count-->0&&len>0); if (count==0) exit(0); //中止程序运行 cp=new char[len+1]; cin>>cp; cout<<cp<<endl; delete []cp; }
动态内存分配程序设计 • 实验任务1:编程实现可变长度的数组,并按要求设置断点,理解指针的运用。 #include <iostream.h> void main() { int n,*ip,*tp; cin>>n; //输入所需数组的长度 ip=new int[n]; //此处设置断点观察ip的值, ip有什么意义? for (int k=0;k<n;k++) cin>>ip[k]; //在此处设置断点观察ip, k的变化 for (tp=ip;tp<ip+n;tp++) cout<<*tp<<‘ ‘; //设置断点观察tp变化 delete []ip; } //在此处设置断点,观察ip的值,说明什么?
二、模板的运用 • 重用:重复使用某些部件 • 重用可在多个级别上进行,形式多样。 • 函数调用:多次使用的程序段单独写为函数,通过调用实现代码重用 • 类的合成(组合) : 在类的数据成员中使用另一个类的对象, 运用另一个类的功能. • 类的继承: 派生类包含基类, 把基类的函数作为派生类函数的一部分. • 模板技术: 模板类; 模板函数 ∨ ∨ ∨
含整数类型的类: class intClass{ int datax,datay; public: //构造 intClass(const int ¶x,const int ¶y) :datax(parax),datay(paray){ } //接口 int Getx(){ return datax; }//读出 int Gety(){ return datay; } void Setx(const int &p){ datax=p; }//设置 void Sety(const int &p){ datay=p; } };
含实数类型的类: class floatClass{ float datax,datay; public: floatClass(const float ¶x,const float ¶y) :datax(parax),datay(paray){ }//构造 float Getx(){ return datax; }//读出 float Gety(){ return datay; } void Setx(const float &p){ datax=p; }//设置 void Sety(const float &p){ datay=p; } };
int float int float int float float int float int int float int float 建立一个模板类,所用类型以参数代替使用时:从一个模板生成含有特定类型的类 class MyClass{ Type datax,datay; public: MyClass(const Type ¶x,const Type ¶y) :datax(parax),datay(paray){} Type Getx(){ return datax; } Type Gety(){ return datay; } void Setx(const Type &p){ datax=p; } void Sety(const Type &p){ datay=p; } };
template <class Type> • 语法要求: • 类定义开始前的模板、类型参数表 • Template <class T> • 类定义内部可用T表示类型 • 模板类型的使用:整体类型名包括 • MyClass<T> • 实例化时(定义对象),用一个已知的类型代替T class MyClass{ Type datax,datay; public: MyClass(const Type ¶x,const Type ¶y) :datax(parax),datay(paray){} MyClass(const MyClass<Type> &p) :datax(p),datay(p){} Type Getx(){ return datax; } Type Gety(){ return datay; } void Setx(const Type &p){ datax=p; } void Sety(const Type &p){ datay=p; } };
C++的模板机制 • 把类定义或函数定义内使用的类型参数化,这些类型参数暂时作为占位符。 • 在用户使用这些类或函数时,参数化的类型会被绑定在实际类型上,这些实际类型可以是C++定义的类型,也可以是用户自定义类型。 • C++的类模板机制为 “泛型”设计提供了可能。 • 泛型:可操作于各种数据类型上的程序设计。(类型的抽象)
实例类的类型名: MyClass<float> 实例类的类型名: MyClass<int> int float int float int float float int int float float int float int int float • 模板关键字:Template • 类型形参用<>括起 • Class表明Type解释为类型名 template <class Type> class MyClass{ Type datax,datay; int flag; public: MyClass(const Type ¶x,const Type ¶y) :datax(parax),datay(paray){} MyClass(const MyClass<Type> &p) :datax(p),datay(p){} Type Getx(){ return datax; } Type Gety(){ return datay; } void Setx(const Type &p){ datax=p; } void Sety(const Type &p){ datay=p; } }; 不需要替换的类型 类型参数在MyClass使用时被具体类型替换。 使用MyClass: MyClass<float> obj_i(1.5, 2.6); 使用MyClass: MyClass<int> obj_i(10,20);
定义数组类模板 • 提取不同类型的数组类定义时共性的代码 • class Array{ //模板类型名与类型参数无关 • T *p; • unsigned len; • public : • Array(unsigned size){ • p=new T[len=size]; • assert(p!=NULL); //判断指针是否有效 • for (unsigned k=0;k<len;k++) p[k]=50; • } • }; template <class T>//增加类型参数 • 考虑字符型数组类的规格: • class charArray{ • char *ip; • unsigned ilen; • public : • chartArray(char *p, unsigned size){ • ip=new char[size]; • assert(p!=NULL); • for (k=0;k<size;k++) ip[k]=p[k]; • } • 考虑实型数组类的规格: • class floatArray{ • float *fp; • unsigned ilen; • public : • floattArray(float *p, unsigned size){ • ip=new float[size]; • assert(p!=NULL); • for (k=0;k<size;k++) ip[k]=p[k]; • } • 提出需求 • 考虑整型数组类的规格: class intArray{ int *ip; unsigned ilen; public : intArray(int *p, unsigned size){ ip=new int[size]; assert(p!=NULL); for (k=0;k<size;k++) ip[k]=p[k]; }
使用: 实例类型要有显式具体类型说明:<int>,<float>,<char> void main(){ Array<int> arr_i(5); Array<float> arr_f(5); Array<char> arr_c(5); for (int k=0;k<5;k++) cout<<arr_i[k];cout<<endl; for (k=0;k<5;k++) cout<<arr_f[k]; cout<<endl; for (k=0;k<5;k++) cout<<arr_c[k]; cout<<endl; }
实例化char类型 实例化int类型 实例化float类型 函数模板:求最大值的函数模板 template <class T> T max(T a, T b) { if (a>b) return a; //假设类型T的关系运算已定义 else return b; } void main() { int a=10, b=20; float x=2.5, y=5.4; char ch1=‘f’, ch2=‘g’; cout<<max(a,b)<<endl; cout<<max(x,y)<<endl; cout<<max(ch1,ch2)<<endl; }
编译处理注意的问题: • 模板类的说明与成员函数的定义要放在一个.h头文件中,应用程序必须用包含#include形式把模块类的完整定义包含进来 • 原因:模板类实例化时编译器必须看到模板类中成员函数的定义形式,以便确定有关操作的可行性、进行类型操作的检查。