Download presentation
Presentation is loading. Please wait.
Published byἌδραστος Μεσσηνέζης Modified 6年之前
1
第1章 C++概述 知识点 面向对象程序设计的基本概念 C++的起源和特点 C++源程序的构成 C++在非面向对象方面的一些特性
难点 面向对象的概念 C++在非面向对象方面的特性
2
要求 掌握: 面向对象的概念 C++程序的格式与结构特点 了解: 类和对象的概念 面向对象的特性 C++在非面向对象方面的一些特性
3
或许你已经学过C语言或Pascal语言,能用这些语言编写简单程序,解决某些具体问题。但在实际应用中,特别是要编制一些比较大型的程序或系统软件时,就会感到仅有这些是不够的,需要有新的设计方法来提高编程能力,以便适应软件开发规模日益庞大的趋势。20世纪90年代以来,在计算机软件行业,面向对象程序设计思想方法已被越来越多的软件设计人员所接受。它是目前最先进的计算机程序设计思想和理念,这种新的思想更接近人的思维活动,利用这种思想和方法进行程序设计时,可以极大地提高编程能力,减少软件维护的开销。C++能完美地体现面向对象的各种特性。
4
1.1 面向对象程序设计的基本概念 面向对象的设计思想是在原来结构化程序方法基础上的一个质的飞跃,是一种新的程序设计理念,是软件开发的一种方法,其本质是把数据和处理数据的过程当成一个整体——对象。 面向对象程序的主要结构特点是:一,程序一般由类的定义和类的使用两部分组成,在主程序中定义各对象并规定它们之间传递消息的规律;二,程序中的一切操作都是通过面向对象发送消息来实现的,对象接受到消息后,启动有关方法完成相应的操作。 面向对象设计的最大优点就是软件具有可重用性。当人们对软件系统的要求有所改变时,并不需要程序员做大量的工作就能使系统做相应的变化。
5
1.1.1 类与对象 类与对象是面向对象程序设计中最重要的概念,如果要掌握面向对象程序设计技术,首先必要很好的理解这两个概念。 对象
从概念上讲,对象代表着正在创建的系统中的一个实体。在日常生活中,对象就是我们认识世界的基本单元,对象是现实世界中的一个实体,整个世界就是由各种各样的对象构成的,例如:一个人,一辆汽车,一个足球等等。 类 类是对象的模板,是对一组具有共同的属性特征和行为特征的对象的抽象。例如:由一个个大学生构成的“大学生”类,而其中的每一个大学生是“大学生”类的一个对象。一个类的所有对象都有相同的数据结构,并且共享相同的实现代码。 类和对象之间的关系是抽象和具体的关系。
6
1.1.2 面向对象的特性 面向对象系统中最主要的特性是封装性、继承性和多态性。 封装性
在面向对象程序设计中,数据的抽象是在确定类时强调对象的共同点而忽略了它们的不同点的结果。数据的封装则是隐藏了数据的内部实现细节的结果,将数据抽象的外部接口与内部的实现细节清楚的分开。
7
继承性 以面向对象程序设计的观点来看,继承所表达的是对象与类之间的关系。这种关系使得某类对象之间可以继承另外一类的特征和能力。继承机制为程序提供了一种组织,构造和重用类的手段。继承使一个类(基类)的数据结构和操作被另一个类即派生类重用,在派生类中只需描述其基类中没有的数据和操作。这样一来,就避免了公用代码的重复开发,减少了代码和数据冗余。
8
多态性 面向对象程序设计中的多态性是指不同的对象收到相同的消息时所产生多种不同的行为方式。C++语言支持两种多态性,即编译时的多态性和运行时的多态性。编译时的多态性通过重载来实现。运行时的多态性是通过虚函数来实现的,程序运行的到底是函数的那个版本,需要在运行时通过对象发送的消息来确定。
9
1.1.3 面向对象程序设计语言 我们要进行面向对象程序设计,必须使用面向对象程序设计语言。面向对象程序设计语言应该具备以下特征:
(1)它支持对象的概念(包括对象的 所有特性,如封装) (2)它要求对象属于类 (3)它提供继承机制
10
1.2 C++的起源和特点 C++的起源 1980年,美国贝尔实验室的Bjarne Stroustrup博士在C语言的基础上,开发出一种过程性与对象性相结合的程序设计语言。这种语言弥补了C语言存在的一些缺陷,并增加了面向对象的特征,1983年,这种语言正式定名为“C++”。
11
“C”语言是C++语言的基础,最初用作UNIX操作系统的描述语言。C语言功能强、性能好,支持结构化程序设计,又能像汇编语言那样高效,伴随着UNIX的成功和广泛使用,诞生后立即获得了广泛的好评和支持。到了上个世纪80年代,C语言己经广为流行,成为一种应用最广泛的程序设计语言。 但是C语言也存在着一些局限: (1) C的类型检查机制相对较弱,使得程序中的一些错误不能在编译时由编译器检查出来。 (2) C缺乏支持代码重用的语言结构。 (3) C不适合开发大型程序,当程序的规模达到一定的程度时,程序员很难控制程序的复杂性。
12
C++正是为了解决上述问题而设计的。C++继承了C的精髓,如高效率、灵活性等,并增加了面向对象机制,弥补了C语言不支持代码重用的不足,这对于开发大型的程序非常有效。C++成为一种既可用于表现过程模型,又可用于表现对象模型的优秀的程序设计语言。
13
1.2.2 C++的特点 C++语言现在得到了越来越广泛的应用,它除了继承C语言的优点之外,还拥有自己独到的特点,最主要的有:
(l) C++保持与C兼容,这就使许多C程序代码不用修改就可以为C++所用,特别是一些用C编写的库函数和实用软件可以用于C++中。 (2) 用C++语言编写的程序可读性更好,代码结构更为合理。 (3) C++生成代码的质量高,运行效率仅比汇编代码慢10%到20%。 。
14
(4) 从开发时间、费用到形成的软件的可重用性、可扩充性、可维护性和可靠性等方面有了很大的提高,使得大型的程序开发变得更加容易。
(5) 支持面向对象的机制。 总之,目前C++的优点正越来越得到人们的认可和推崇,它已经成为被广泛使用的通用程序设计语言。在国内外使用和研究C++的人数正迅猛增加,优秀的C++版本和配套的工具软件也不断涌现
15
1.3 C++源程序的构成 C++程序的一般格式 C++是C的一个超集,它几乎保留了C的所有特性。下面通过一个求两个数较大值的简单的C++程序,来对C++程序的格式有一个初步的了解。
16
例1-1 //max.cpp #include <iostream.h> int max(int a,int b); //函数原型的说明 void main( ) //主函数 { int x,y,temp; //定义二个整型变量 cout<<"Enter two numbers:"<< "\n"; //提示用户输入两个数 cin>>x; //从键盘输入变量x的值 cin>>y; //从键盘输入变量y的值 temp=max(x,y); //调用max函数,将得到的值给变量temp cout<<"The max is:"<<temp<< "\n" ; //输出两个数中较大的值 } int max(int a,int b) //定义max函数,函数值为整型 int c; //定义一个整型变量 if(a>b) c=a; else c=b; //判断两个数的大小,将较大值赋给c return c; //将c的值返回
17
本程序用来计算两个整数中较大的值。它由两个函数组成:主函数main( )和被调用函数max( )。函数max( )的作用是判断a与b的大小,把其中较大的值赋给变量c。return语句把c的值返回给主函数main( )。返回值通过函数名max带回到main( )函数的调用处。 程序的第1行是注释语句,由"//"开始,到行尾结束,这条注释语句注明了本程序的文件名为max.cpp。
18
程序的第2行是预编译命令#include <iostream. h>,它的作用是,指示编译程序把头文件iostream
程序的第2行是预编译命令#include <iostream.h>,它的作用是,指示编译程序把头文件iostream.h包含进#include命令所在的源程序中。 iostream.h是C++系统定义的一个头文件,用于定义程序的输入和输出。第7行中出现的“cout”语句的作用是,在屏幕上显示出字符串"Enter two numbers:","\n"是换行符,即输出上述信息后回车换行。
19
第8行和第9行中的“cin”的作用是分别输入变量x和y的值,即执行cin后,把从键盘输入的两个数值分别赋给变量x和y。
第10行用来调用max函数,调用时把实际参数x和y的值传给函数add()中的形式参数a和b,执行max函数后得到一个返回值(即max函数中的c),把这个值赋给temp,然后第11行输出temp的值。程序运行情况如下: Enter two numbers: 3↙ 5↙ The max is:5
20
1.3.2 C++程序的结构特点 通过上面的例子,我们可以看出C++程序的结构有以下特点:
(1)C++程序由一组函数组成,函数是构成C++程序的基本单位。其中有且仅有一个名为main的函数,称为主函数。程序运行时第一个被执行的函数必定是主函数,不论它在程序的什么部位。被调用的函数可以是系统提供的库函数,也可以是用户自己编写的函数(例如上面例子中的函数max())。对于用户自己定义的函数,使用前应提供“声明”,如上面例子中的“int max(int a,int b);”。
21
(2)C++函数由函数的说明部分和函数体两部分组成。
这部分包括函数名、函数类型、函数参数(形式参数)及其类型。例如在例2-l中的max() 函数的说明部分为: int max ( int a, int b) ↑ ↑ ↑ ↑ ↑ ↑ 函数类型 函数名 形参类型 形式参数 形参类型 形式参数
22
函数类型规定为函数返回值的类型,如int,float等。无返回值的函数是void类型。
函数可以没有参数,但对于无参函数,函数名后面的圆括号不能省略。 函数体 函数说明部分下面的花括号内的部分称为函数体。函数体中的内容也就是函数的定义部分,主要是给出该函数的功能和执行流程。
23
(3)C++中每一个语句和数据定义必须以分号结束。一行程序内可以写多个语句,一个语句也可以分写在多行上。
说明: (l)C源程序文件扩展名为.c,而C++源程序文件扩展名为.cpp。 (2)常用的C++版本,如Visual C++或borland C++都带有C和C++两种编译器,当源程序文件扩展名为.c时,启动C编译器,当源程序文件扩展名为.cpp时,启动C++编译器。
24
1.4 C++在非面向对象方面的一些特性 C++是从C发展而来,因此C程序中大部分的特点和功能,在C++中仍可以使用。但C++语言还增加了很多C语言不具备的新特性,这些特性中除了“面向对象”的概念外,同时也包括一些非面向对象的新特性。我们下面就来介绍这些非面向对象的新特性,它们使得C++程序比C程序更简洁,安全,强大。
25
1.4.1 注释 在C语言中,我们用"/*"及"*/"作为注释分界符号,例如: /* This is just a
注释 在C语言中,我们用"/*"及"*/"作为注释分界符号,例如: /* This is just a test for program */ C++语言保留了这种注释方式,同时还增加了另一种注释方式,该注释以"//"开始,到行末结束。例如: Temp = a+b; //This is just a comment "//…"注释方式适合于注释内容不超过一行的注释,使用很简洁方便。
26
I/O流 C语言中进行输入输出,是依靠系统提供的函数来完成,如标准输入和输出函数scanf和printf。相比C语言,C++使用了更安全和强大的方法来进行输入/输出操作,也就是“流”的概念。例如: int I; double f=8.5; cin>>i; cout<<f; 这里的cin是标准输入流,在程序中用于代表标准输入设备,即键盘。运算符“>>”称为“提取运算符”,表示将从标准输入流(即键盘)读取的数值传送给右方指定的变量。也就是说,对于语句“cin>>i;”,用户从键盘输入的数值会自动地转换为变量i的数据类型,并存入变量i内。类似于C语言中的scanf(“%d”,&i);。 运算符“>>”允许用户连续输入一连串数据,例如: cin>>a>>b>>c; 它将按顺序从键盘上接收所要求的数据,并存入对应的变量中。两个数据间用空白符(空格、回车或Tab键)分隔。
27
cout是标准输出流,在程序中用于代表标准输出设备,通常指屏幕。运算符“<<”称为“插入运算符”,表示将右方变量的值显示在屏幕上。例如,执行下面的语句后:
cout<<f; 变量f的值将显示在屏幕上。类似于C语言中的printf(“%f”,f);。f必须是基本数据类型,而不能是void类型。运算符“<<”允许用户连续输出一连串数据,也可以输出表达式的值,例如: cout<<a+b<<c; 它将按照顺序将数据依次输出到屏幕上。
28
说明: (1)程序中如果需要使用cin或cout进行输入/输出操作时,则程序中必须嵌入头文件iostream.h,否则编译时要产生错误。下面是一个输入输出流的例子: 例1-2 #include <iostream.h> void main() { char name[20]; cout<<"please input your name:"; cin>>name; cout<<name<<endl; }
29
(2)在C++程序中,我们仍然可以用C语言的传统方式进行输入输出操作,即沿用stdio函数库中的I/O函数,如printf()函数、scanf()函数或其它的C输入输出函数。
(3)在C中,常用"\n"实现换行,C++中增加了换行控制符endl,其作用与"\n"一样。它的使用很方便,只要插入在输出语句中需要换行的相应位置即可。例如以下两个语句的操作是等价的: cout<<"x="<<x<<endl; cout<<"x="<<x<<"\n";
30
灵活的局部变量说明 在C语言中,所有的局部变量说明必须置于可执行代码段前面,而不允许局部变量的说明出现在可执行代码的中间或后面。如在C中,下面的程序段是不正确的: void func() { int m; m=8; int n; n=4; }
31
C语言的编译器在编译时会指示有错,因为其中变量定义语句“int n;”插在“m=8;”这句可执行语句之后了。但在C++语言中,这是允许的,也就是说上面的程序编译时不会出错。
32
1.4.4 const 运算符 在C语言中,经常使用宏定义,也就是#define来定义常量,例如: #define time lO0;
而C++语言提供了一种更灵活、更安全的方式来定义常量,即用const修饰符来定义常量。const型的常量相比#define的宏定义要灵活的多,同时提供了更强大的安全性。const可以创建有类型的常量,例如: const int time = l00; 这样定义后的time的值将不能修改。 这里要注意的是,用const定义的常量必须在声明的时候初始化它的值,并且一旦初始化完成后,该值将不能再修改。
33
const也可以与指针一起使用,根据const出现的位置不同,分为几种情况:
(1)指向常量的指针,例如: const char* name="stone", //声明指向常量的指针 这个语句的含义为:声明一个名为name的指针变量,它指向一个字符型常量,初始化name指向字符串"stone"。使用const定义之后,不允许改变name指针所指的常量的值,因此以下语句是错误的: name[3]=’a’; 但是可以改变name指针本身的值。例如下列语句是允许的: name="John"; 该语句赋给了指针另一个常量,即改变了name的值。
34
(2)常指针 常指针是指把指针本身,而不是它指向的对象声明为常量,例如: char* const name="stone"; //常指针 (注意:const在语句中出现的位置与(1)中不同) 这个语句的含义为:声明一个名为name的指针变量,该指针是指向字符型数据的常指针,用"stone"的地址初始化该常指针。 一个常指针一旦创建,则它指向的地址是固定的,也就是说指针本身将不能移动了。但是该地址的存储内容,也就是常指针所指的数据可以改变,例如: name[3]=‘a'; //合法 name="John"; //出错 第一个语句改变了常指针所指的数据,这是允许的;但第二个语句要改变指针本身,这是不允许的。
35
(3)指向常量的常指针 这样的指针本身不能改变,它所指向的值也不能改变。要声明一个指向常量的常指针,二者都要声明为const,例如: const char* const name="stone"; //指向常量的常指针 这个语句的含义是:声明了一个名为name的指针变量,它是一个指向字符型常量的常指针,用"stone"的地址初始化该指针。根据这个指针的定义,可以判断出下面二个语句都是错误的: name[3]=‘a'; //出错,不能改变指针所指的值 name="John"; //出错,不能改变指针本身
36
说明: (1)如果用const定义一个整型常量,关键字int可以省略。所以下面的两行定义是等价的: const int time=l00; const time=l00; (2)与#define定义的常量有所不同,const定义的常量可以有自己的数据类型,这样C++的编译程序可以进行更加严格的类型检查,也使得程序的安全性得到提高。因此建议用const取代#define定义常量。 (3)函数参数也可以用const说明,用于保证实参在该函数内部不被改动。
37
内联函数 内联函数是C++新增加的机制,只要在函数说明前加上关键字"inline",则该函数就被声明为内联函数。当程序中出现对内联函数的调用时,C++编译器将使用函数体中的代码替代函数调用表达式,这样可以加快代码的执行,减少调用开销。下面的程序定义了一个内联函数。
38
例1-3 #include <iostream.h> inline int rect(int m) //定义函数rect为内联函数 { return m*m; } void main( ) for(int i=1;i<=3;i++) cout<<"i="<<i<<" area="<<rect(i)<<endl; 程序运行结果如下: i=1 area=1 i=2 area=4 i=3 area=9
39
说明: (1)内联函数在被调用之前必须进行函数定义,否则编译器不知道应该插入什么代码。内联函数通常在main函数前面定义。 (2)通常只有较短的函数才定义为内联函数,对于较长的函数,最好作为一般函数处理。因为若内联函数较长且调用太频繁时,程序会加长很多。 (3)使用内联函数也可以替代宏定义,内联函数具有宏定义的所有优点而避免了宏定义的不安全性。 (4)内联函数的定义中不允许使用循环语句和switch语句。 注:后面将学习到,在C++的类体内定义的成员函数都是内联函数。
40
作用域运算: : C++语言中,提供了一个作用域运算符“∷”,用来标识某个变量的作用域。通常情况下如果程序中存在两个同名变量,一个是全局的,另一个是局部的,那么局部变量在其作用域内具有较高的优先权。
41
例1-4 #include <iostream.h> int temp=5; //全局变量temp void main( ) { int temp; //局部变量temp … temp=50; cout<<"temp is"<<temp<<endl; //输出的是局部变量temp的值 } 程序执行结果如下: temp is 50
42
在main( )函数的输出语句中的变量temp是main( )函数内定义的局部变量,因此打印的是局部变量temp的值50。
如果希望在局部变量的作用域内使用同名的全局变量,可以在该变量前加上作用域运算符“∷”,此时∷temp代表全局变量temp。
43
请看下面的例子。 例1-5 #include <iostream.h> int exam; void main( ) { exam=40; //局部变量exam ::exam=10; //全局变量exam cout<<"local exam="<<exam<<endl; cout<<"global exam="<<::exam<<endl; } 程序运行结果为: local exam=40 global exam=l0 注:在后面会学习到,作用域运算符::在类的定义中,还可以用来标识某个成员属于哪个类。
44
函数的缺省参数 函数的缺省参数,也称为默认参数。在C++中,对函数进行说明时,可以为一个或多个参数指定缺省的参数值,当调用此函数时,如果没有提供某个参数的实参值,C++会自动地以缺省值作为其相应参数的值。
45
例如函数原型说明为: int func(int x=3,float y=4.5); 则x与y的缺省参数值分别为3与4.5。 当进行函数调用时,编译器按从左向右的顺序匹配实参和形参,若程序中没有指定足够的实参,则编译器按顺序用函数原型中的缺省参数值来补足所缺少的实参。例如以下的函数调用都是允许的: func(100,79.8); //x=100,y=79.8 func(25); //x=25,y=4.5 func( ); //x=3,y=4.5
46
说明: (1)所有有缺省值的参数,都必须出现在无缺省值的参数的右边,否则会在函数调用时产生错误。例如下列函数说明是错误的: int fun(int i,int j=5,int k); 因为在取缺省参数的int j=5后,不应再说明非缺省参数int k。若改为: int fun(int i,int k,int j=5); 则是正确的。 (2)在函数调用时,若某个参数省略,则其后的参数皆应省略而采用缺省值。不允许某个参数省略后,再给其后的参数指定参数值。例如不允许出现以下调用func()函数的语句: func( ,30.5);
47
1.4.8 强制类型转换 在C语言中,如果要把一个整数(int)转换为浮点数(float),要求使用如下的格式: int i=8;
强制类型转换 在C语言中,如果要把一个整数(int)转换为浮点数(float),要求使用如下的格式: int i=8; float x=(float)i; 在C++语言中,也支持这样的语法格式,但提供了一种更为方便的方式,即将数据类型名称作为函数名使用,使得类型转换的执行看起来好像调用了一个函数。如上面的语句可改写成: float x=float(i); 以上两种方法在C++中都可以,但第2种方式更明晰和直观。
48
函数重载 在C语言中,函数名必须是惟一的,也就是说不允许出现同名的函数。这种要求有时显得比较麻烦,比如当要求编写求整数、浮点数和双精度数的绝对值函数时,若用C语言来处理,必须编写三个不同名的函数,例如: abs(int i); //求整数的绝对值 fabs(float i); //求浮点数的绝对值 dabs(double i); //求双精度数的绝对值
49
当使用这些函数求某个数的绝对值时,必须根据待求数的不同数据类型,调用合适的函数,这样,虽然这三个函数的功能是相同的,但用户必须记住三个函数名。增加了用户记忆的负担,并影响程序的可读性。
而在C++中,提供了函数重载的机制。重载的含义是允许两个或者两个以上的函数可以使用相同的函数名,只要保证函数参数的类型或个数不同。当两个或者两个以上的函数共用一个函数名时,称为函数重载。被重载的函数称为重载函数。
50
由于C++支持函数重载,上面三个求绝对值的函数可以起一个共同的名字abs,但它们的参数类型必须保持不同。当用户调用这些函数时,编译器会自动根据实参的类型,来确定到底调用哪个重载函数。因此用户使用求绝对值的函数时,只需记住一个abs函数即可,其余的就不需要操心了。上述例子我们可以用下面的程序来实现。
51
例1-6 #include <iostream.h> int abs(int i) { if(i>0) return i; else return –i; } float abs(float i) double abs(double i) void main( ) { int i = -8; float f = -9.5; double d = 12.54; cout<<i<<abs(i)<<endl; cout<<f<<abs(f)<<endl; cout<<d<<abs(d)<<endl; } 程序运行结果如下: -8 8
52
在main( )中三次调用了abs( )函数,实际上是调用了三个不同的重载版本。系统根据传送的不同实参的类型来决定调用哪个重载函数。例如当调用abs(i)时,因为i为整型变量,所以系统将调用求整数绝对值的重载版本int abs(int i)。可见,利用重载概念,用户在调用函数时,书写非常方便。
53
我们再通过一个求多边形周长的函数定义,来看一个参数个数不同的重载函数例子:
例1-7 #include <iostream.h> int length(int x,int y,int z) //求三角形的周长 { return x+y+z; } int length(int x,int y,int z,int m) //求四边形的周长 return x+y+z+m; void main( ) int a=3,b=5,c=7,d=8; cout<<”The length of trigle is:”<<length(a,b,c)<<endl; cout<<”The length of four is:”<<length(a,b,c,d)<<endl; 程序运行结果如下: The length of trigle is:15 The length of four is:23
54
函数length( )被重载,两个重载函数的参数个数不同。编译程序根据实参的个数目决定调用哪一个函数。
说明: (1)函数的返回类型不同不能作为函数重载的条件,重载函数必须保证参数个数或参数类型不同,否则编译程序将无法确定调用哪一个重载版本。例如: int length(int x,int y); double length(int x,int y); 虽然这两个函数的返回类型不同,但参数个数和类型完全相同,因此编译程序将无法区分这两个函数。
55
(2)一般而言,重载函数应执行相同或类似的功能,例如abs( )函数一般用来返回一个数的绝对值,如果重载abs( )函数而改变它的功能,比如让它计算一个数的开方,则虽然语法上没错,但是不恰当,也容易让读程序的人产生误解,我们应该在编程中注意避免。
56
用new和delete分配动态内存 动态内存是在程序运行时(而非编译时)分配的内存空间,C语言提供了两个函数malloc( )和free( )分别用于申请和释放动态内存。而C++提供了一对运算符new和delete,分别用来进行动态内存的申请和释放。相比malloc( )和free( )函数,new和delete的功能要更为强大和安全,使用也更加方便。
57
new用于内存分配,它其的使用形式为: P = new type; 其中,type是一个数据类型名,P是指向该数据类型的指针。new从动态内存中为程序分配一块sizeof(type)字节大小的内存,然后返回一个指向该内存首地址的指针,并将该指针的值赋值给指针P。 运算符delete用于释放new分配的存储空间。它的使用形式一般为: delete P; 该语句执行后,P指针指向的动态内存将会得到释放。
58
下面是new和delete操作的一个简例:
例1-8 #include <iostream.h> void main( ) { int *p; //声明一个整型指针变量p p=new int; //申请一块存储整数的动态内存,并将该内存//首地址赋给p *p=10; cout<<*p<<endl; delete p; //释放p指向的动态内存间 } 程序执行结果如下: 10
59
该程序定义了一个整型指针变量p,然后用new为其分配了一块动态内存,p指向这个内存块。然后在这内存块中赋予初值10,并将其值输出。最后通过delete释放p指向的存储空间。
int *p = new int[10]; 这时new为具有10个元素的整型数组分配动态内存空间,并将首地址赋给了指针p。 释放动态分配的数组存储区时,必须在指针名前加上“[ ]”表示释放的是数组空间,如: delete [ ]p; 注:在[]内不需要标明数组的大小。
60
new和delete相比malloc( )和free( ),有以下几个优点:
(1)new可以自动计算所要分配内存的类型的大小,而不必使用sizeof( )来计算所需要的字节数,减少了发生错误的可能性,也为程序员提供了便利。 (2)new能够自动返回正确的指针类型,不必对返回指针进行类型转换。 (3)new和delete可以被重载,由用户自定义内存分配方式。 (4)使用new申请动态内存时,如果当前没有足够的内存满足申请要求,new将返回空指针(NULL)。为了防止程序出错,提高程序的健壮性,通常要对内存的动态分配是否成功进行检查。请看以下例子。
61
例 1-9 #include <iostream.h> void main( ) { int *q; q=new int; if (q==NULL) cout <<"memory not enough"<<endl; *q=20; cout<<*q<<endl; delete q; } 若动态分配内存失败,程序将在屏幕上显示" memory not enough "。
62
1.4.11 引用 引用是C++语言中的新概念,其功能类似于为变量起一个别名,主要应用于函数参数及函数的返回类型。 1.引用的定义
引用 引用是C++语言中的新概念,其功能类似于为变量起一个别名,主要应用于函数参数及函数的返回类型。 1.引用的定义 用“&”来定义引用,例如: int i=5; int &j=i; //声明j是i的引用 上面的程序,创建了一个整型变量i的引用j,j实际上就相当于i的别名,一旦被声明,就和i占用相同的内存。当i变化时,j也随之变化,反之亦然。换句话说,一旦定义目标的一个引用,则引用和目标就成为了同一个东西,对其中任何一个进行修改,都等于对另一个进行修改。
63
例1-10 #include <iostream.h> void main( ) { int i; int &j=i; i=15; cout<<"i="<<i<<"j="<<j<<"\n"; j=40; cout<<"Address of i"<<&i<<"\n"; cout<<"Address of j"<<&j<<"\n"; } 执行结果: i=15 j=15 i=40 j=40 Address of i i0x0012FF7C Address of j i0x0012FF7C 我们由程序的运行结果可以看出,i和j的值始终是一样的,保持同步更新,且占用内存中的同一地址。
64
说明: (1)定义引用时,必须立即对它进行初始化。例如下述定义是错误的: int i; int &j; //错误 j=i; 为引用提供的初始值,可以是一个变量或另一个引用。例如: int i=5; int &j1=i; int &j2=j1; 这样定义后,变量i将有两个别名:j1和j2。 (2) 引用一旦建立,就不可重新赋值,不能让一个引用再作为另一个变量的别名,例如: int i,k; int &j=i; j=&k; //错误
65
(3)不能建立引用的数组,例如: int b[5]; int &b1[5]=b; //错误 (4)不能建立空引用 int &i=NULL; //错误 (5)不能建立“指向引用的指针”或“引用的引用” int& *p; //错误 int &&r; //错误
66
2. 引用作为函数参数 引用的一个主要用途,是用作函数的参数。通常函数参数的传递有三种方式“值传递”,“指针传递”,“引用传递”。引用传递参数的功能和指针传递很像,而形式类似于值传递,可读性更好。下面我们通过一个例子看看引用传递和指针传递的区别:
67
例1-11 #include <iostream.h> void swap(int *m,int *n) //用于交换两个参数的值 { int temp; temp=*m; *m=*n; *n=temp; } void main( ) int a=5,b=10; cout<<"a="<<a<<"b="<<b<<endl; swap(&a,&b); cout<<"a="<<a<<"b="<<b<<endl; } 程序运行的结果为: a=5 b=l0 a=l0 b=5 采用指针传递参数的方法,调用函数swap( )后,a和b的值被交换了。
68
再来看看用引用传递参数的例子: 例1-12 #include <iostream.h> void swap(int &m,int &n) { int temp; temp=m; m=n; n=temp; } void main() int a=5,b=10; cout<<"a="<<a<<"b="<<b<<endl; swap(a,b); 程序运行结果为: a=5 b=10 a=l0 b=5 当程序中调用函数swap( )时,实参a和b分别对引用m和n进行初始化。根据引用的定义,在swap( )中,对m和n的访问就是对a和b的访问,所以函数swap( )的定义中修改了m和n的值,也就改变了main( )中变量a和b的值。 由此例可以看出,引用传递和指针传递实现的功能一样,但其语法更清楚简单。
69
3.引用返回值 通常情况下,一个函数调用是不能出现在赋值运算符左边的。 引用可以作为函数的返回值,如果一个函数的返回类型是一个引用的话,则该函数的调用可以出现在赋值运算符的左边。 例1-13 #include <iostream.h> int a[ ]= {1,3,5,7,9}; int& func(int i) //定义返回引用的函数 { return a[i]; } void main( ) func(2)=6; //将a[2]重新赋值为6 cout<<func(2)<<endl; 程序运行结果为: 6
Similar presentations