c++基础教案ppt课件_第1页
c++基础教案ppt课件_第2页
c++基础教案ppt课件_第3页
c++基础教案ppt课件_第4页
c++基础教案ppt课件_第5页
已阅读5页,还剩134页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、.,目录,第一节 c+概述 第二节 函数 第三节 类和对象 第四节 友元和重载 第五节 模板 第六节 继承,.,第一节 c+概述,main()函数 标准输入输出流 exit语句 数据类型 标准库string类型 引用类型 指针和const限定符 动态内存分配,.,1.1 main()函数,int main() return 0; 每个c+程序必须含有main()函数,且main函数是唯一被操作系统显式调用的函数 定义函数必须制定4个元素:返回类型、函数名、形参表、函数体。 操作系统通过main的返回值来确定程序是否成功执行完毕,返回0表示程序成功执行完毕,通常非0表示有错误出现,.,1.2 标

2、准输入输出流,C+没有直接定义进行输入/输出的任何语句,这个功能由标准库iostream.h提供。包含两个类:输入流istream和输出流ostream. #include using namespace std; 标准库中的四个IO对象 cin标准输入(如键盘),为istream对象 cout 标准输出(如显示屏),为ostream对象 cerr 标准错误,用于输出警告和错误信息,为 ostream对象 clog用于产生程序执行的一般信息,为ostream对象,.,cin读入流(由键盘输入),作用从键盘取得数据送至内存,与一起使用结合方向为自左向右 例如:int v1,v2; cinv1v2

3、; 从流中读取信息时,输入流缓冲指针跟踪最后一个读入到流的字符,每次尝试从流获取信息时,都从指针的当前位置开始 cin自动跳过空白字符(whitespace) 返回值为左操作数,.,用cout写入到流(输出到屏幕),cout必须与输出操作符一起使用,结合方向为自左向右 例如: coutEnter two numbersendl; cout decx;(hex/oct) 作用将右操作数插入到cout中,可同时接受不同类型的数据输出,所以可有多个操作符,把信息写入流时,把信息添加到流的末尾,相当于从左到右输出 endl(end of line)为操纵符,具有换行效果,并刷新与设备相关联的缓冲区,刷

4、新后用户可立即看到写入到流中的输出。注:忘记刷新可能会造成输出停留在缓冲区,建议在程序员调试过程中,这些语句都应刷新输出流 iomanip.h中,setw(n):为后面的输出项预留n列,.,1.3 exit()语句,形式:exit(interger_value); 执行exit语句时,程序立即终止。一般来说,如果因为一个错误而调用exit,就使用1,其他情况使用0。 该函数在头文件cstdlib中,所有要有预编译命令#include using namespace std;,.,1.4 内置数据类型,常量 宏常量:#define PI 3.1415926 系统不为其分配内存,只是简单的字符串替

5、换 const常量:const 类型 常量标识符=值; 常量定义后不能修改,所以必须初始化 例如: const double PI=3.1415926; 系统为PI分配内存单元 两种常量的比较 const常量有数据类型,而宏常量没有数据类型;编译器可以对const常量进行类型合法性检查,而对宏常量仅仅是字符的替换,没有合法性检查,并且在字符替换的时候可能产生意想不到的错误 c+中,const常量完全可以取代宏常量,.,布尔类型bool,它的值只有两个true和false 可以将算术类型的任何值赋给bool对象。0代表false 非0代表true 初始化:创建对象并给它赋初值(赋值指擦除对象的当

6、前值并用新值代替) 有两种形式:复制初始化:用等号 int val=1024; 直接初始化:初始化式放在括号中 int val(1024); 通常在一个对象首次使用的地方定义该对象,.,1.5 标准库string类型,#include using namespace std; string对象的定义和初始化 string s2(s1); /s1为string对象或字符串字面值 string s3(n,c);/s3为n个c 注意:字符串字面值和标准库string类型不是同一类型 string对象的读写 输入:cins; /从第一个非空字符读至下一个空白字符 读入一行getline(cin,lin

7、e);两个参数:输入流对象和string对象 功能:从输入流的中读取内容到line中,换行符是该行的结束标志 注:getline()不忽略开头的换行符,但line并不保存换行符,即若开头遇换行符,line为空string 输出:coutsendl;,.,#include #include using namespace std; int main(int argc, char* argv) string str; coutstr; / 输入str的时候,只要遇到了空格,就会结束输入 coutstr; 输入了一次回车,这个语句遇到回车就结束,这样,回车符也跟着到了内存,到用getline()再输

8、入时,内存里第一个字符就是回车,所以getline()一读到这个回车就结束了,所以getline(cin,str);不起作用。 解决: 处理掉cinstr;遗留的回车符,可以在该语句下加一句: getchar();/吸收内存里的回车符,.,string对象的操作 s.empty(); 若字符串为空,返回true s.size(); 返回s中字符的个数 sn;返回s中位置为n的字符 s1=s2;比较,所有的比较运算符都可以使用 s1+=s2; 连接 s1=s2;赋值,string类型可以和字符串字面值连接,赋给string类型,但是,+操作符的左右操作数必须至少有一个string类型 strin

9、g s=hello+,+s1;,.,1.6 引用类型,引用就是对象的另一个名字,主要用于函数的形参 引入 void swap(int a , int b) int temp=a; a=b; b=temp; int main() int x=10, y=20; swap(x,y); 形参与实参有各自不同的内存空间,若实参是一个复杂对象,重新分配内存会引起程序执行效率大大下降 形参对实参为值传递,对形参的任何改变不会引起实参值的改变,.,1.6.1 非const引用,引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。 声明:类型标识符 /定义引用ra,它是变量a的引用(别名

10、) (1) 1.引用必须在定义时初始化, int ,.,1.6.2 const引用,格式: const 类型标识符 /正确 引用型参数应该在能被定义为const的情况下,尽量定义为const 。,.,对const对象的引用,const int ival=1024; int i=34; const int refval是对const型的引用,所以任何对refval的赋值都是非法 const对象必须用const引用 const引用可以绑定到相关的类型的对象,或绑定到右值,.,1.6.3 指针和引用的比较,指针和引用都可间接访问另一个值,但是 1、引用总是指向某个对象,定义时必须初始化。 指针则可以

11、在任何时候被初始化 2、引用一旦被初始化,就不能改变引用的关系 而指针则可以随时改变所指向的对象 3、赋值:引用即为别名,给引用赋值修改的是该引用所关联的对象的值(非const引用)而指针可以更改其指向的对象,也可以更改所指向的对象的值 4、不能有NULL引用,引用必须与合法的存储单元关联,而指针则可以是NULL,.,1.7 指针和const限定符,使用const修饰指针时,由于const的位置不同,而含意不同。 1.7.1 指向const对象的指针 指向const的指针,不可以通过该指针修改对象,但可以其他方式修改 double dval=3.14,pi=3.1415; const doub

12、le *ptr = / /合法,.,若一个指针是指向const对象,则该指针必须具有const特性。例如 const double pi=3.14159; const double *ptr= /错误,const对象要用指向const的指针来指向,这样可以保证既不能通过*ptr,也不能通过pi修改其值 指向const的指针常用作函数的形参,这样可以确保函数的实参在函数调用过程中不被修改 void use_ptr(const int *p),.,1.7.2 const指针,固定指向一个对象的指针,即指针本身是常量 char * const ptr1 = stringptr1;/const放在类型

13、说明和变量之间 ptr1 = stringptr2; /非法,指针本身的值不可改变*ptr1 = m; /合法 指针所指的变量的值可以改变 若指针及指针所指向的变量的值都不可以更改 const char * const ptr=stringptr;,.,1.8 动态内存分配,一 内存分配有三种方式 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存

14、容量有限。 堆,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。,.,1.8.2 单个对象的动态分配与释放,动态分配:由关键字new及其后面的类型指示符构成。该类型指示符可以是内置类型或class类型。 例:new int ;从堆中分配了一个int型的对象。 new Student ;分配了一个Student类对象。 需要注意的是堆中分配的对象没有名字。new表达式返回了一个指向该对象的指针,对该对象的全部操作都要通过这个指针间

15、接完成。 例如: int *pi = new int ; 该new表达式创建了一个int型的对象,由pi指向它。 初始化 int *pi = new int (0) ; 该语句表示pi指向一个int型的对象,该对象的初始值为0。括号中的表达式被称作初始化式,.,动态内存的释放,与静态分配内存的对象不同,编译器不会自动释放它们所占的内存-除非整个程序结束。所以当动态分配内存的对象完成它的使命,需要被销毁的时候不能依赖编译器,而要靠程序员用delete释放。 格式: delete pi;/释放pi所指向的内存空间 指针pi本身是个在全局域中声明的全局对象,它的生命期由编译器控制。pi的存储区在程序

16、开始之前就被分配,一直保持到程序结束。 而pi指向的对象的生命期是由程序员控制的,它是在程序执行过程中遇到new表达式时才被创建,遇到delete表达式时被销毁并收回存储区,.,动态内存的释放,delete只能用在指向内存是用new动态分配的指针上,如果将其在指向堆以外内存的指针上,会使程序运行期间出现未定义的行为。唯一的例外是,当指针指向NULL时,不管指针指向的对象是如何分配内存的,都不会引发麻烦。 void f() int i; char *str = asdd; int *pi = /安全!pd指向一个动态分配的对象 ,.,常见错误,1忘记了释放内存,造成内存泄露。含有这种错误的函数每

17、被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存泄漏(memory leak)。 函数体内的局部变量在函数结束时自动消亡。例如p是局部的指针变量,它消亡的时候会让它所指的动态内存一起消亡。这是错觉! void Func(void)char *p = (char *) malloc(100);/ 动态内存会自动释放吗?指针消亡了,并不表示它所指的内存会被自动释放 2 对同一内存区应用了多次delete表达式。这通常发生在多个指针指向同一个动态分配对象的时候。若多个指针指向同一对象,当通过某一个指针释放该对象时就会发生这种情况。 3内存被释放

18、了,并不表示指针会消亡或者成了NULL指针,形成野指针,.,野指针,因此,当程序执行delete pi;语句时,pi指向对象的内存被释放,但指针pi本身的内存并没有受到delete表达式的影响。在delete pi;之后,pi被称作空悬指针(俗称野指针),即指向无效内存的指针。空悬指针是错误的根源,它很难被检测到,如果对它进行操作将会产生无法预测的结果。一个比较好的办法是在指针所指的对象被释放后,马上将该指针设置为NULL,这样可以清楚地表明该指针不再指向任何对象 char *p = (char *) malloc(100);strcpy(p, “hello”);free(p); / p 所指

19、的内存被释放,但是p所指的地址仍然不变if(p != NULL)/ 没有起到防错作用 strcpy(p, “world”);/ 出错,.,野指针,指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:class A public:void Func(void) cout Func();/ p是“野指针”,.,1.8.3 数组的动态分配与释放,new表达式也可以在堆中分配数组。在这种情况下new表达式中的类型指示符后面必须有一对方括号,里面的值代表数组的长度,而且该数值可以是一个复杂的表达式。new表达式返回指向数组第一个元素的指针。 动态分配数组只需指定类型和长度 格式: new

20、typesize; /size为数组元素的长度,可以是任意表达式。new返回指向新分配数组的第一个元素的指针。例如: int *p2; p2=new intn; delete 释放new分配的存储空间 delete p;/表明p指向的是动态存储区的数组,如果遗漏,编译器无法发现错误,.,int *pi = new int (1024) ; /分配单个int型的对象,用1024初始化 int *pia = new int 1024 ; /分配一个含有1024个元素的int型数组,未被初始化,.,第二节 函数,参数传递 函数返回值 内联函数 函数的重载,.,2.1 参数传递,C+语言中,函数的参数

21、和返回值的传递方式有三种:值传递、指针传递和引用传递。在调用函数时,对于每一个实参,其类型必须与对应的形参类型相同,或可被转换为该形参类型 2.1.1 普通的非引用形参 为传值调用,调用时将实参的值赋给形参,而形参的任何改变不会改变实参 void swap(int p1, int p2) int temp=p1; p1=p2; p2=temp; 调用时:int a=5,b=9;swap(a,b); / a、b并没有互换,.,指针形参,指针传递传递的是地址,函数可以通过指针赋值,修改指针所指向的对象的值 void swap(int *p1, int *p2) int temp=*p1; *p1=

22、*p2; *p2=temp; 调用时怎么写? 1、若要保护指针指向的对象的值,则形参需定义为指向const对象的指针 void use_ptr(const int *p) 在函数体中,不能通过*p修改p指向的对象的值 实参既可以为int *,也可以为const int *类型,.,2、如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,调用结束释放所以该参数本来就无需保护,没有必要加const修饰。例如不要将函数void Func1(int x) 写成void Func1(const int x)。同理不要将函数void Func2(A a) 写成void Func2(con

23、st A a)。其中A为用户自定义的数据类型。 对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为void Func(A cinab; /输入a,b两变量的值swap(a,b); /直接以变量a和b作为实参调用swap函数 couta b; /输出结果 (1)被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。(2)使用引用传递函数的

24、参数,在内存中并没有产生实参的副本,它是直接对实参操作;在传值调用中若传递的是对象,将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。,.,三种传递方式的比较,值传递 void Func1(int x)x = x + 10; /形参的使用 int n = 0;Func1(n); /调用 cout “n = ” n endl;/ n = 0 指针传递 void Func2(int *x)(* x) = (* x) + 10;/形参的使用 int n = 0; /调用Func2(/ n = 10,.,引用传递: void Func3(int / n

25、= 10对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?答案是“用适当的工具做恰如其分的工作”。,.,const 引用,若不希望在函数体中通过引用来修改实参,可以使用const引用 int search(const T 一般将不需要修改的引用形参都定义为const引用,而且可以增强其灵活性。(对于非const形参,实参不能为const对象,也不能为字面值常量),.,传递指向指针的引用,void swap(int* ,.,2.2 函数返回值,在系统调用处将函数的返回值放在一个临时空间

26、,在调用的地方使用该返回值(赋给一个变量或进行运算),调用语句执行完,释放临时空间 1)返回引用:没有复制返回值,相反返回的是对象本身 格式:类型标识符 一般来说,函数返回的引用是对函数的某个参数的引用,而这个参数本身也是引用类型。,.,引用作为返回值,必须遵守以下规则:不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了无所指的引用,程序会进入未知状态。 const string ,.,2.3 内联函数,在程序执行函数调用时,要求在转去前要保护现场保存地址,转回后先要恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间开销,影响其效率。特

27、别是对于一些函数体代码不是很大,但又频繁地被调用的函数来讲,解决其效率问题更为重要。因此引入内联函数。 内联函数的定义方法定义内联函数的方法很简单,只要在函数定义的头前加上关键字inline即可。,.,inlineisNumber(char ch);/函数原型声明inline即内联函数 voidmain() char ch; cin.get(ch); if (isNumber(ch) cout=0 使用inline修饰带来的好处就是在每个调用isNumber(ch)的地方都换成了(ch=0 / 可以改为 void Eat(Beef ); void EatFish(); / 可以改为 void

28、Eat(Fish ); void EatChicken(); /改为 void Eat(Chicken );,.,例如:int sum(int a, int b) double sum(double a, double b) 调用重载函数时,编译器会根据实参的类型、个数、顺序去调用相应的函数。 int sum1=sum(5,6); double sum2=sum(3.4, 5.6); 注:仅仅返回类型不同是不行的,.,第三节 类和对象,类的定义和实现 类的静态成员 类的对象的定义和使用 类的成员函数 构造函数与析构函数,.,第三节 类和对象,类是一种复杂的数据类型,它是将不同类型的数据和与这些

29、数据相关的操作封装在一起的集合体。这有点像C语言中的结构,唯一不同的就是结构没有定义所说的“数据相关的操作”,即“方法” 封装 外部用户只能看到定义抽象行为的操作集合 程序员通过数据变量维护对象内部的状态,.,3.1 类的定义和实现,类的定义一般地分为说明部分和实现部分。说明部分是用来说明该类中的成员,包含数据成员的说明和成员函数的说明。“干什么”实现部分是用来对成员函数的定义。 “怎么干”。 类的一般定义格式如下: class 类名 / class是定义类的关键字,是种标识符,通常用字母C开头 public: 公有成员函数或数据成员的说明private: 私有数据成员或成员函数的说明 ; /

30、注意:分号不可缺,.,类的定义,访问权限修饰符或访问控制修饰符:公有的(public)、私有的(private)和保护的(protected) public:公有部分往往是一些操作(成员函数),它是提供给用户的接口功能。这部分成员不仅可以在类内被访问,也可以在类外被访问。 private:私有部分通常是一些数据成员,描述该类中的对象的属性,用户是无法访问它们的,只有成员函数或经特殊说明的函数才可以引用它们,它们是被用来隐藏的部分。 protected:可以被本类及其继承类使用,.,一个日期类定义的例子,/类的说明部分,一般放在date.h文件中 class Cdate public: void

31、 setDate(int y, int m, int d); int IsLeapYear(); void Print(); private: int year, month, day; ; /类的实现部分 一般放在date.cpp文件中 #include “date.h” void Cdate:setDate(int y, int m, int d) 这里出现的作用域运算符: year = y; month = m; day = d; 是用来标识某个成员函数是属于哪 int Cdate:IsLeapYear() 个类,如果成员函数定义在类体外 要使用作用域运算符: return(year%4

32、=0 ,.,类的定义说明,定义类时应注意的事项 1)在类体中不允许对所定义的数据成员进行初始化。 2)一般地,在类体内先说明公有成员,它们是用户所关心的,后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。 3)习惯地将类定义的说明部分放在头文件中 .h。而实现部分放在 .cpp文件中,.,3.1.2 类的实现,类成员函数的实现部分一般放在.cpp文件中,如果要在类外使用类中的实例变量或者在类外定义函数体,要使用作用域运算符,用来标识某个成员是属于哪个类的。 格式 返回值类型 类名:函数名(参数列表) 例如: void Cdat

33、e:setDate(int y, int m, int d) year = y; month = m; day = d; ,.,作用域运算符,class PlayingCard public: enum Suits Spade, Diamond, Club, Heart; Suits suit () return suitValue; int rank () return rankValue; private: Suits suitValue; int rankValue; boolean faceUp; ; 如:在类定义之外使用表示花色的常量 if(aCard.suit()=PlayingC

34、ard:Diamond) ,.,3.2 类的静态成员,被一个类的所有实例共享的公共数据成员。静态成员(static)可以实现同一个类的多个对象之间的数据共享。 静态数据成员对于类的所有对象只开辟了一块内存空间(在静态存储区),其中一个对象对其改变,保证所有对象存取的是更新后的相同的值。 静态数据成员的使用方法和注意事项如下:1、定义:静态数据成员在定义或说明时前面加关键字static。2、初始化:格式如下::=: 初始化在类体外进行,前面不加static和访问权限控制符private,public等。 初始化时使用作用域运算符来标明它所属类,因为静态数据成员是类的成员,而不是对象的成员。 静态

35、数据成员是静态存储的,必须对它进行初始化。 3、引用 :,.,class Myclasspublic:Myclass(int a, int b, int c);void GetNumber();void GetSum();private:int A, B, C;static int Sum; 实现int Myclass:Sum = 0;Myclass:Myclass(int a, int b, int c)A = a;B = b;C = c;Sum += A+B+C; 调用Myclass M(3, 7, 10),N(14, 9, 11);,.,3.3 类的对象的定义和使用,对象的定义有两种方法

36、 class 类名 对象名; 2.也可以先声明类,然后定义类的对象 类名 对象名; 编译系统为对象分配内存并且将这段内存空间与对象名称进行绑定,并进行必要的初始化操作. 变量声明只是创建了一个标识变量的名称,大部分面向对象语言通过new操作符来创建对象,c+语言可以将他们结合起来 如 PlayingCard aCard ; /在java中必须写成PlayingCard aCard=new PlayingCard() ;,.,使用对象:在调用成员函数时,必须指明对类的哪个对象进行操作。 利用成员运算符 . 表明从属关系,用于类的对象 例如: Cdate date,*p; date.setDate

37、(2007, 7, 20); p=,.,3.4 类的成员函数,成员函数的定义和声明:类的所有成员必须在类定义的内声明。类的成员函数既可以在类内定义,以可以在类外定义,他可以访问该类的private成员 如:bool same_isbn(const SalesItem 没有前缀的isbn指的是用于调用函数的对象的isbn 调用 调用函数时,实际是使用对象来调用的if(total.same_isbn(trans) /r是trans的引用,.,this指针,每个成员函数都有一个额外的、隐含的形参将该成员函数与调用该函数的类对象绑定在一起,这个形参为this。调用成员函数时,形参this初始化为调用函

38、数的对象的地址。所以total.same_isbn (trans)可以理解为 same_isbn(,.,当成员函数中函数参数与数据成员同名时,需要this指针 class PlayingCard PlayingCard (int suit,int rank) this-rank=rank; this-suit=suit; this-faceUp=true; ,.,const成员函数,使用const关键字进行说明的成员函数为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。 说明格式如下:类型说明符 函数名 (参数表) const;其中

39、,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。 任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。,.,class Stack public: void Push(int elem); int Pop(void); int GetCount(void) const; / const成员函数 private: int m_num; int m_data100; ; int Stack:GetC

40、ount(void) const + m_num; / 编译错误,企图修改数据成员m_num Pop(); / 编译错误,企图调用非const函数 return m_num; ,.,3.5 构造函数与析构函数,概述 构造函数的初始化表 缺省的构造函数 缺省的拷贝构造函数 运算符重载 缺省的赋值函数 析构函数,.,3.5.1 概述,每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C+编译器将自动为A产生四个缺省的函数,如 A(); / 缺省的无参数构造函数 A(const A / 缺省的赋值函数,

41、.,作用:把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。当对象被创建时,构造函数被自动执行。当对象消亡时,析构函数被自动执行。这样可以自动执行对象的初始化和清除工作。 命名:构造函数、析构函数与类同名,由于析构函数的目的与构造函数的相反,就加前缀以示区别。 构造、析构函数没有返回值,即使是void也不可以。 构造、析构函数置于类定义的public部分 析构函数不得带有任何参数 构造函数可以有一个或多个,一旦定义一个有参的构造函数,一定要显式定义一个无参构造函数,.,3.5.2 构造函数的初始化表,和其他函数不同,构造函数有个特殊的初始化方式叫“初始化表达式表”(初始化表)。初始化

42、表位于函数参数表之后,以冒号开始,后面接数据成员列表,列表之间用逗号分隔,每一个变量后面用一对圆括号包含它的初始值 。 初始化表放在构造函数的定义中 PlayingCard :PlayingCard (Suits is, int ir) : suit(is), rank(ir),faceUp(true) /函数体 ,.,(2) 执行顺序,首先执行初始化列表,然后执行该构造函数函数体中的语句 成员对象初始化的次序完全不受它们在初始化表中次序的影响,只由成员对象在类中声明的次序决定。这是因为类的声明是唯一的,而类的构造函数可以有多个,因此会有多个不同次序的初始化表。如果成员对象按照初始化表的次序进

43、行构造,这将导致析构函数无法得到唯一的逆序。 例 class x int i; int j; public: x(int val):j(val),i(j) /相当于i=j;j=val;j还没有初值就被使用 ; 所以尽量避免使用成员来初始化其他成员 x(int val):j(val),i(val),.,const数据成员,const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。class Acons

44、t int SIZE = 100; / 错误,企图在类声明中初始化const数据成员int arraySIZE;/ 错误,未知的SIZE; const数据成员的初始化只能在类构造函数的初始化表中进行,类的定义class A A(int size);/ 构造函数 const int SIZE ; ;类的实现A:A(int size) : SIZE(size)/ 构造函数的初始化表 生成类的对象 A a(100);/ 对象 a 的SIZE值为100 A b(200);/ 对象 b 的SIZE值为200,.,(3)使用说明,大多数成员可以在构造函数的初始化表中初始化,也可以在函数体中赋值。但是 类的

45、const常量和引用成员只能在初始化表里被初始化,因为不能对他们赋值,所以要在执行构造函数的函数体之前完成初始化。 class ConstRef public: ConstRef(int ii); private: int i; const int ci; int ,.,ConstRef:ConstRef(int ii) i=ii; ci=ii; /不能对常量赋值 ri=i; /此时是对ri所绑定的对象赋值,但是还不知道ri是谁的别名 ConstRef:ConstRef(int ii):i(ii),ci(ii),ri(i),.,3.5.3 缺省的构造函数,格式:A:A() 当类没有定义构造函数

46、时,编译器才会自动生成一个不带参数的缺省构造函数 在缺省构造函数中,对于类中的成员,若为类类型,调用默认构造函数来初始化;若为内置或复合类型(指针、数组),若对象定义在局部作用域中,必须对它赋初值 使用缺省构造函数创建对象 Complex c; 注: Complex c();是错误的。编译器把他解释为一个函数的声明。c是函数名,返回类型为Complex 一旦定义一个有参的构造函数,一定要显式的定义一个无参构造函数,.,Complex:Complex(double r , double i) real=r; image=i; Complex:Complex(double r) real=r; i

47、mage=0; Complex:Complex() real=0; image=0; cout“Initializing 0 0”endl; 调用方式: 创建对象时自动调用缺省的构造函数 Complex c1; Complex *pc1 = new Complex; /注意,调用缺省构造函数时,没有括号 调用有参构造函数 Complex c2(4.5); 或 Complex c2=new Complex(4.5); /一个参数 Complex c3(4.5 , 6.2); 或 Complex c3=new Complex(4.5,6.2);,.,3.5.4 缺省的拷贝(复制)构造函数,格式:

48、A(const A ,.,在类中,初始化对象有两种方式 直接初始化:用括弧直接调用与实参匹配的构造函数。如:string dots(10,.); string empty; 复制初始化:用等号首先使用指定构造函数创建一个临时对象,然后使用复制构造函数将那个临时对象复制到正在创建的对象。 string empty_copy=string(); 复制构造函数的应用场合 定义一个对象并用一个同类型对象初始化时 类的对象作为实参传递给形参时 从函数返回时复制一个对象 即使是定义了其他的构造函数,这个缺省的复制构造函数也是存在的,.,3.5.5 运算符重载,对于基本数据类型,有许多相应的基本运算,如算术

49、运算的+、-、*、/,但是对用户自定义类型,却不可以用这些运算符进行运算,为了使类类型也能使用这些运算符,必须对这些运算符进行重载 重载操作符是具有特殊名称的函数,在C+语言中,可以用关键字operator加上运算符来表示函数名,叫做运算符重载。 遵循的规则: *不能改变原运算符操作数的个数 + + *不能改变原运算符的自然含义 *重载操作符必须具有至少一个类类型或枚举类型的操作数,既不能对内置类型对象的操作符重载。 不能重载的运算符:成员运算符. 作用域运算符: 条件运算符?:一元运算符*,.,如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个参数,因为对象自己成了左

50、侧参数。从语法上讲,运算符既可以定义为友元函数,也可以定义为成员函数。 作为类的成员函数重载的格式: 一元运算符 返回值类型 类名:operator运算符() /operator运算符作为一个整体作为函数名 二元运算符 返回值类型 类名:operator运算符(参数说明) /参数说明只允许有一个参数,即右操作数,.,例如:对复数进行相加,通过+号能直接实现 类内声明: Complex operator+(const Complex ,.,3.5.6 缺省的赋值函数,格式:A ,.,拷贝构造函数与赋值函数的比较,拷贝构造函数和赋值函数非常容易混淆,常导致错写、错用。拷贝构造函数是在对象被创建时调

51、用的,而赋值函数只能被已经存在了的对象调用。 String a(“hello”); String b(“world”); String c = a; / 调用了拷贝构造函数,最好写成 c(a); c = b; / 调用了赋值函数,.,赋值和复制,内存分配方法影响赋值的含义: 1. 复制语义:c+中使用,实际是调用的赋值函数,右侧变量值赋给左侧变量,两个变量占用不同存储空间,值是独立的 2. 指针语义:同一(Java中使用)左侧变量的地址赋给了右侧变量的地址,两个变量是共用同一地址,其中一个变量值的改变会改变另一个变量,.,3.5.8 析构函数,在对象脱离其作用域时,系统自动执行析构函数,作善后

52、清理工作,主要用于资源回收 析构函数与类同名,无参数,无返回值。只是在函数名前加 例:Complex:Complex() 编译器总会自动生成一个析构函数,他按成员在类中的声明次序逆序撤销成员。默认析构函数释放对象生命期内或构造函数中获取的资源。 注:动态分配的对象只有在指向该对象的指针被删除时才会撤销,若没有删除,不会运行该对象的析构函数,对象一直存在,会导致内存泄漏 当对象的引用或指针超出作用域时,不会运行析构函数。当删除指向动态分配对象的指针或实际对象超出作用域时,才会运行析构函数,.,第四节 友元和重载,友元 友元函数 友元类 友元关系与继承 重载与友元 重载 重载输出运算符 重载输入运

53、算符 转换与类类型,.,4.1 友元,我们已知道类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。 为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它

54、破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。,.,4.1.1 友元函数,友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。1、友元函数友元函数的特点是他是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样。下面举一例子说明友元函数的应用。,.,#include using namespace std;classPointpublic:Point(doublexx,doubleyy)x=xx;y=yy;voidGetxy();frienddoubleDistance(Point,.,voidm

55、ain()Pointp1(3.0,4.0),p2(6.0,8.0);p1.Getxy();p2.Getxy();double d=Distance(p1,p2); /友元函数被调用时与普通函数一样coutDistanceisdendl; 说明:类中说明了一个友元函数Distance(),说明时加friend关键字,标识它不是成员函数,而是友元函数。 定义方法与普通函数定义一样,而不同于成员函数的定义,因为它不需要指出所属的类。但是它可以引用类中的私有成员 调用友元函数,也是同普通函数的调用一样,不要像成员函数那样调用。,.,4.1.2 友元类,友元还可以是类,即一个类可以作另一个类的友元。当一

56、个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数。 class Base friend class Frnd;/声明Frnd为Base的友元类 protected: int i; ; class Frnd public: int mem(Base b)return b.i; /Frnd中可以访问Base的私有变量 int mem(D1 d)return d.i; ;,.,4.1.3 友元关系与继承,友元关系不能继承。基类的友元对派生类的成员没有特殊的访问权限。同样,如果某一基类具有特殊的访问权限,该基类的派生类不能访问授予友元关系的类 class D1 : pub

57、lic Base protected: int j; ; class D2 :public Frnd public: int mem(Base b)(return b.i;,.,4.1.3 重载与友元,两个复数类型相加,可以定义重载函数为Complex的成员函数。但是,如果+前的操作数不是Complex类型,我们可以采用友元函数解决这个问题 类内声明: friend Complex operator +(double d, const Complex 运算符规则所有的一元运算符建议重载为成员函数 = () -只能重载为成员函数二元操作符+= -= /= *= type可以是内置类型名、类名等,

58、但是,一般不允许转换为数组或函数类型,转换为指针和引用类型是可以的 转换操作符是一种特殊的类成员函数。它的功能是将类类型的值转换为其他类型的值。 转换操作符在类定义体内声明,在operator后跟转换的目标类型 不能指定返回类型,形参表必须为空,.,class SmallInt friend ostream 转换函数一般不应该改变被转换的对象,所以转换操作符通常应被定义为const成员,.,该转换是用户为类类型对象自定义的转换,但是调用时是由系统隐式调用 SmallInt si1,si2; int main() 输入si1、si2的值; cout127)?”greater than”: (si1127)?”less than”:” equal to”); “127n”; ,.,第五节 模板,在创建完成抽象操作的函数时,如:拷贝,反转和排序,你必须定义多个版本以便能处理每一种数据类型。以 max() 函数为例,它返回两个参数中的较大者: double max(double first, double second);complex max(complex first, complex second

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论