Upload
atara
View
140
Download
7
Embed Size (px)
DESCRIPTION
《 C++ 语言程序设计 》 第二次直播课堂 主讲 首都经贸大学 李宁 副教授. 一、关于函数原形和头文件. 函数的定义和函数的原型 例: double cubic ( double d){ // 定义 return d*d*d; } double cubic ( double d); // 原形. 函数原形提供了生成函数调用代码所必须的接口信息,因此: 在函数的调用处之前,需通过函数原形提供该调用函数的接口信息。. 函数定义也能提供同样的接口信息,因此: 如果函数的定义出现在函数调用处之前,函数原形即可省略。. - PowerPoint PPT Presentation
Citation preview
22
一、关于函数原形和头文件一、关于函数原形和头文件• 函数的定义和函数的原型函数的定义和函数的原型例:例:
double cubicdouble cubic (( double d){ //double d){ // 定义定义 return d*d*d;return d*d*d;}}double cubicdouble cubic (( double d); //double d); // 原形原形
77
• 应尽可能做到函数的实现(定应尽可能做到函数的实现(定义)与函数的使用(调用)的义)与函数的使用(调用)的分离:分离:–首先设计函数原形,作为函数定义和函数调用必须共同遵守的接口标准;–将定义函数的程序和使用函数的程序放在不同文件中。
88
• 头文件以固定的、便于利用的形头文件以固定的、便于利用的形式保存一组函数的调用接口信息,式保存一组函数的调用接口信息,确保了接口信息描述的正确性和确保了接口信息描述的正确性和一致性。一致性。
• 用用 #include#include 命令将头文件插入到命令将头文件插入到需要的程序文件中需要的程序文件中
1010
例例 11 ::template<class T1, class T2, class T3>template<class T1, class T2, class T3>
T2 Max(T1 x, T3 y){ T2 Max(T1 x, T3 y){ return T3(T3(x)>T3(y)? x : y); return T3(T3(x)>T3(y)? x : y);} //} // 定义定义
cout<<Max<int,foat>(3,3.5); //cout<<Max<int,foat>(3,3.5); // 调用 调用 提示:未出现在模板函数参数表中提示:未出现在模板函数参数表中的模板参数,其实参不得省略。的模板参数,其实参不得省略。
1111
template <typename Type,template <typename Type, int Rows,int Cols> int Rows,int Cols> void sumAll(Type data[][Cols],void sumAll(Type data[][Cols], Type result[]){ Type result[]){ for(int i=0;i<Rows;i++){ for(int i=0;i<Rows;i++){ result[i]=0; result[i]=0; for(int j=0;j<Cols;j++) for(int j=0;j<Cols;j++) result[i]+=data[i][j];} result[i]+=data[i][j];}} //} // 定义定义
例例 22 (教材例(教材例 5.105.10 ):):
1212
int d[][3]={int d[][3]={ {1,2,3},{2,3,4}, {1,2,3},{2,3,4}, {4,5,6},{6,7,8}}; {4,5,6},{6,7,8}};int r[4];int r[4];sumAll<int,4,3>(d,r);//sumAll<int,4,3>(d,r);// 调用调用
提示:对于普通类型(非虚拟类提示:对于普通类型(非虚拟类型)的模板参数,其实参不得省略。型)的模板参数,其实参不得省略。
1313
template<int Rows,int Cols,template<int Rows,int Cols, typename Type> typename Type>.. .. .. //.. .. .. // 定义定义
.. .. .. .. .. .. sumAll<4,3>(d,r); //sumAll<4,3>(d,r); // 调用调用
提示:通过调整模板参数的顺序,提示:通过调整模板参数的顺序,有可能使更多的模板实参可以省略。有可能使更多的模板实参可以省略。
例例 33 ::
1414
三、关于参数表中的数组三、关于参数表中的数组• 参数表中的数组就是指针参数表中的数组就是指针• 因此,定义参数表中的数组时不因此,定义参数表中的数组时不必给出第一维的大小;必给出第一维的大小;• 因此,对于参数表中的数组,也因此,对于参数表中的数组,也可以定义为指针,对应关系是:可以定义为指针,对应关系是: A[] A[] ←→ ←→ *A; *A;
A[][]… A[][]… ←→ ←→ (*A)[]… (*A)[]…
1515
• 因此,调用这样的函数时,只需因此,调用这样的函数时,只需以数组名作为实参;以数组名作为实参;• 因此,修改形参数组就是在修改因此,修改形参数组就是在修改形参指针所指向的数据,也就是形参指针所指向的数据,也就是在修改实参数组。但这并不违反在修改实参数组。但这并不违反“传值”原则,因为对应于形参“传值”原则,因为对应于形参的实参数组名(指针)并没有被的实参数组名(指针)并没有被修改;修改;
1616
四、动态空间的使用应避免的四、动态空间的使用应避免的问题问题•悬挂访问:通过一个没有初始化悬挂访问:通过一个没有初始化
的指针或空指针访问不存在的数的指针或空指针访问不存在的数据;据;
•存储泄漏:申请的动态空间用完存储泄漏:申请的动态空间用完后被丢弃,没有释放;后被丢弃,没有释放;
1717
•重复释放:同一动态空间被多次重复释放:同一动态空间被多次释放;释放;
•申请操作与释放操作不配套申请操作与释放操作不配套例如用 例如用 malloc() malloc() 申请,用 申请,用 ddeleteelete 释放;或用 释放;或用 new new 申请,申请,用 用 free() free() 释放。释放。
2222
class XX{class XX{ int xx;int xx; public:public: XX(int n=0):xx(n){} XX(int n=0):xx(n){}
...... };}; class YY{class YY{
XX xd;XX xd; double yy;double yy; public:public: YY(double d):yy(d){}YY(double d):yy(d){}//YY(…):xd( ),yy(d){}//YY(…):xd( ),yy(d){} ......}; };
隐含调用无参隐含调用无参构造函数例构造函数例 11
2323
class YY{class YY{ XX xd;XX xd; double yy;double yy; public:public: YY(double d):yy(d){}YY(double d):yy(d){} … …};};
class ZZ:public YY{class ZZ:public YY{ long k;long k; public:public: ZZ(long m):k(m){} //ZZ(long m):k(m){} // 错误!错误! … …};};
隐含调用无参隐含调用无参构造函数例构造函数例 22
2525
–在调用一个具有类对象参数的函数时,系统隐含地以实参对象为参数调用拷贝构造函数,创建形参对象。
–对于返回值为类对象的函数,在用return 返回时,系统隐含地以标 return 语句中的对象为参数调用拷贝构造函数,创建作为返回值的对象。
2626
–每个类至少要有一个拷贝构造函数。如果没有定义,系统自动生成一个默认的拷贝构造函数,它以“浅层复制”的方式构造新对象。
–对于必须通过“深层复制”才能正确构造相同对象的情况,应当定义专门的拷贝构造函数。
2727
假定假定class String{class String{ char *p;char *p;public:public: String(const char *c){p=c;}String(const char *c){p=c;} ……};};中没有定义拷贝构造函数,中没有定义拷贝构造函数,
2828
则执行则执行 String s1("String s1("This is a stringThis is a string");"); String s2(s1); String s2(s1);的效果是:的效果是:
3030
在 在 String String 中增加如下的拷贝构中增加如下的拷贝构造函数,实现造函数,实现“深层复制”“深层复制” : :String::String(String &s){String::String(String &s){ p=new char[strlen(s.p)+1]; p=new char[strlen(s.p)+1]; strcpy(p,s.p); strcpy(p,s.p);}; }; 这样,执行同样的语句序列的效果这样,执行同样的语句序列的效果是是::
3131
““深层复制”示深层复制”示意意TT hh ii ss ii ss aa ss tt rr ii nn gg
TT hh ii ss ii ss aa ss tt rr ii nn gg
p
s2
p
s1
3333
String& String::operator =String& String::operator = (const String &s){ (const String &s){ if(p) delete []p; if(p) delete []p; p=new char[strlen(s.p)+1]; p=new char[strlen(s.p)+1]; strcpy(p,s.p); strcpy(p,s.p); return *this; return *this;};};
3636
视同常量视同常量+ - == …+ - == …其他其他 视同变量视同变量任意任意**间接访问间接访问 无无&&取地址取地址 ----后减后减 11 视同常量视同常量++++后增后增 11
+= *= …+= *= …复合赋值复合赋值 ==赋值赋值 ----前减前减 11 视同变量视同变量有有变量变量
或或视同变量视同变量
++++前增前增 11操作结果操作结果副作用副作用第一操作数第一操作数操作符操作符操作名称操作名称
3737
七、关于操作符重载七、关于操作符重载•应保持操作符原有的基本语义,应保持操作符原有的基本语义,重载的操作符应体现为原操作重载的操作符应体现为原操作符功能在新的数据类型中的延符功能在新的数据类型中的延伸伸
•应尽量保持操作符原有的特性应尽量保持操作符原有的特性
3838
–优先级、结合性和操作数个数这三个特性自动得以保持
–+= 、 *= 、 ++ (前增 1 )等操作符要求第一操作数必须是变量;当作为非成员函数重载这类操作符时,为了达到同样效果,第一参数应说明为引用
3939
–= 、 += 、 *= 、 ++等具有副作用的操作符,除后增 1 、后减 1 之外,其操作结果视同变量。当重载这类操作符时,为了达到同样效果,返回值应说明为引用, return 语句应返回第一参数(对于非成员函数重载),或返回 *this(对于成员函数重载)
4040
例如,例 例如,例 8.1 8.1 的 的 Fraction Fraction 类类(分数类)中重载了前增(分数类)中重载了前增 11 操作操作符:符:
Fraction &opertor++(){Fraction &opertor++(){ nume+=deno; nume+=deno;
return *this;return *this;
}}
4242
•操作符的重载应当配套操作符的重载应当配套–如果重载了 + 、 - ,应考虑同时
重载 ++ 、 --–如果重载了 == ,应应考虑同时
重载 !=–如果重载了 > ,应应考虑同时重
载 <……
4646
•使用引用参数还是非引用参数?使用引用参数还是非引用参数?–使用引用参数可减少参数传递过程中的数据复制量
–如果操作符作为非成员函数重载,且该操作符要修改第一操作数(如 += ),则代表第一操作数的第一参数必须是引用参数
4747
–使用非引用参数可利用只需一个实参的构造函数实现类型的自动转换
例:例 例:例 8.1 8.1 中 增加构造函数:中 增加构造函数: Fraction(int n=0,int d=1)Fraction(int n=0,int d=1) :num(n),deno(d){ :num(n),deno(d){ FracSimp(); FracSimp();} }