C++面向对象(二):C++ 概述

C++面向对象:C++ 概述

会有点长,不过读过就全学会喽!!!!!!
会有点长,不过读过就全学会喽!!!!!!
会有点长,不过读过就全学会喽!!!!!!

1.C + + 的起源和特点

1.1 C + + 的起源

C + + 是美国贝尔实验室的 Bjarne Stroustrup 博士在 C 语言的基础上, 弥补了 C 语言 存在的一些缺陷,增加了面向对象的特征, 于 1980 年开发出来的一种过程性与对象性结 合的程序设计语言。最初他把这种新的语言叫做“含类的 C”,到 1983 年才取名为C + + 。
C 语言是 1972 年由 Dennis Richie 在贝尔实验室设计的一个通用目的程序设计语言, 它的前身是 B 语言, 而 B 语言又是在继承和发展了 BCPL 语言的基础上设计的, C 最初用 作 UNIX 操作系统的描述语言。开发者希望它功能强、性能好,能像汇编语言那样高效、 灵活, 又能支持结构化程序设计。由于这一追求的实现并随着 UNIX 的成功和广泛使 用, C 语言被介绍于世并立即赢得了青睐, 到了 80 年代已经广为流行, 成为一种应用最广 泛的程序设计语言。
但是 C 语言也存在着一些局限:

  1. C 语言的类型检查机制相对较弱, 这使得程序中的一些错误不能在编译阶段由 编译器检查出来。
  2. C 语言本身几乎没有支持代码重用的语言结构。
  3. C 语言不适合开发大型程序, 当程序的规模达到一定的程度时, 程序员就很难控 制程序的复杂性。

C + + 正是为了解决上述问题而设计的。C + + 继承了 C 的原有精髓, 如高效率、灵 活性;扩充增加了对开发大型软件颇为有效的面向对象机制; 弥补了 C 语言不支持代码 重用、不适宜开发大型软件的不足, 成为一种既可用于表现过程模型, 又可用于表现对象 模型的优秀的程序设计语言之一。

1.2 C + + 的特点

C + + 现在得到了越来越广泛的应用, 它继承了 C 语言的优点, 并有自己独到的特 点,最主要的有:

  1. C + + 保持与 C 兼容, 这就使许多 C 代码不经修改就可以为 C + + 所用, 用 C 编 写的众多的库函数和实用软件可以用于 C + + 中。
  2. 用 C + + 编写的程序可读性更好, 代码结构更为合理, 可直接地在程序中映射问 题空间的结构。
  3. 生成代码的质量高, 运行效率仅比汇编语言代码段慢 10%到 20%。
  4. 从开发时间、费用到形成的软件的可重用性、可扩充性、可维护性和可靠性等方 面有了很大的提高,使得大中型的程序开发变得更加容易。
  5. 支持面向对象的机制, 可方便地构造出模拟现实问题的实体和操作。 总之,目前人们对 C + + 的兴趣越来越浓, 它已经成为被广泛使用的通用程序设计语 言。在国内外使用和研究 C + + 的人正迅猛增加, 优秀的 C + + 版本和配套的工具软件 不断涌现。

2.C + + 源程序的构成

2.1 C + + 程序的一般格式

C + + 是 C 的一个超集,它几乎保留了 C 的所有的特性。下面我们给出一个简单的 两数相加 C + + 程序,以便读者对 C + + 程序的格式有一个初步的了解。

// sum .cpp 
#include <iostream.h> 
int add(int a , int b) ; // 函数原型的说明 
int main () // 主函数 
{
	int x, y, sum; // 定义三个整型变量 
	cout << ”Enter two numbers:<<′\ n′; // 界面: 提示用户输入两个数 
	cin >> x; // 从键盘输入变量 x 的值 
	cin >>  y; // 从键盘输入变量 y 的值 
	sum = add( x, y) ; // 调用函数 add,将得到的值赋给变量 q 
						// sum 
	cout << ”The sum is:<< sum <<′\ n′; 
	// 输出两个数的和 sum 的值 
	return 0 ; 
}
int add(int a , int b) // 定义 add 函数, 函数值为整型 
{
	int c; // 定义一个整型变量 
	c = a + b; // 计算两个数的和
	return c; // 将 c 的值返回, 通过 add 带回调用处 q 
}
Enter two numbers: 
35↙
The sum is: 8

程序的第 1 行是 C + + 风格的注释语句, 它由“/ / ”开始, 到行尾结束, 这条注释语句 注明了本程序的文件名为 sum .cpp。程序的第 2 行预编译命令 # include < iostream .h > 的作用 是, 指 示 编 译 程 序 把 头 文 件 iostream .h 嵌 入 # include 命 令 所 在 的 源 程 序 中。 iostream .h 是 C + + 系统定义的一个头文件, 它设置了 C + + 风格的 I/ O 环境。第 7 行 cout 语句的作用是将字符串“Enter two numbers:”在屏幕上显示出来“, \ n”是换行符, 即 输出上述信息后回车换行。第 8 行和第 9 行中的 cin 的作用是分别输入变量 x 和 y 的值, 即执行 cin 后, 把从键盘输入的两个数值分别赋给变量 x 和 y。第 10 行用来调用 add 函 数, 调用时把实际参数 x 和 y 的值传给函数 add( ) 中的形式参数 a 和 b, 执行 add 函数后得 到一个返回值(即 add( )函数中的 c) ,把这个值赋给 sum,然后第 11 行输出 sum 的值。

本程序用来计算两个整数的和。它由两个函数组成: 主函数 main ( ) 和被调用函数 add( )。函数 add( )的作用是计算 a 与 b 的和, 把其值赋给变量 c。return 语句把 c 的值返 回给主函数 main( )。返回值通过函数名 add 带回到 main( ) 函数的调用处。

2.2 C + + 程序的结构特点

通过以上例子,可以看出 C + + 程序的结构有以下特点:
( 1) C + + 程序通常由包括 main ( )在内的一组函数组成, 函数是构成 C + + 程序的基 本单位。其中名为 main 的函数称为主函数, 可以将它放在程序的前部, 也可放在程序的 中部或后部。但是,不论主函数放在程序的什么部位, 程序运行时第一个被执行的函数必 定是主函数。因此, 一个可运行的程序必须有主函数。被调用的函数可以是系统提供的 库函数,也可以是用户自己编写的函数 ( 例如上面例子中的函数 add ( ) ) 。对于用户自己 定义的函数,使用前应给予“声明”, 如上面例子中的“int add(int a, int b) ;”。可以说 C + + 是函数式的语言,程序的全部操作都是由函数来完成的。
(2 ) C + + 函数由函数的说明部分和函数体两部分组成。

  • ① 函数的说明部分
    这部分包括函数名、函数类型、函数 参数 ( 形式 参数 ) 及其类 型。 add( )函数的说明部分为:

在这里插入图片描述
函数类型规定为函数返回值的类型, 如 int、float 等。无返回值的函数是 void 类型。 main ( )函数是一个特殊的函数, 可看作是由操作系统调用的一个函数, 其返回值是 void 型或 int 型。当其返回值为 int 型时, 可以使用 return 语句从 main( ) 中返回一个值, 正如 从其它函数中返回值一样。在本例中,返回值为零意味着 main( ) 已被成功地执行。
函数参数可有可无,但函数名后面的圆括号不能省略。

  • ② 函数体
    函数说明部分下面的花括号{……}内的部分称为函数体。如果一个函数内有多对花 括号,则最外层一对{ }为函数体的范围。函数体一般包括:
    a . 变量定义 如例 2 .1 主函数 main( )中的“int x, y,sum;”。
    b . 执行部分 由若干语句组成, 每个语句以分号结束。如例 2 .1 add ( )函数中的“c = a + b; return c;”等。
    在某些情况下,函数可以没有变量定义部分, 甚至可以既无变量定义也无执行部分。
    例如:
    dump( ) { }
    这是一个空函数,不执行任何操作, 但是它是一个合法的函数。

(3 ) C + + 中每个语句和数据定义必须以分号结束。分号是 C + + 的必要组成部分, 即使程序中的最后一个语句也应是分号结束。由于用分号来区分每个语句, 因此几乎可 以用任何格式来书写 C + + 程序。一行内可以写多个语句, 一个语句也可以分写在多行 上。
说明:

  • (1 ) C 源程序文件扩展名为 .c, 而 C + + 源程序文件扩展名为 .cpp。
  • ( 2) 常用的 C + + 版本, 如 Turbo C + + 或 Borland C + + 都带有 C 和 C + + 两种编译 器,当源程序文件扩展名为 .c 时, 启动 C 编译器; 当源程序文件扩展名为 .cpp 时, 启动 C + + 编译器。

3.C + + 在非面向对象方面的一些特性

C + + 是从 C 发展而来, C 程序中的表达式、语句、函数和程序的组织方法等在 C + + 中仍可以使用。C + + 对 C 语言注入了面向对象的新概念, 同时也增加了一些非面向对 象的新特性,这些新特性使 C + + 程序比 C 程序更简洁或更安全。

3.1 注释行

在 C 语言中,我们用“/ * ”及“ */ ”作为注释分界符号, 例如:
*/ * This is a test /
C + + 除保留了这种注释方式外, 还提供了一种更有效的注释方式, 该注释以“/ / ”开 始,到行尾结束。例如以下两条语句是等价的:
x = y + z; / * This is a comment */
x = y + z;/ / This is a comment
C + + 的“/ / …”注释方式特别适合于注释内容不超过一行的注释, 这时, 它显得很 简洁。

说明:

  1. 以/ / 开始的注释内容只在本行起作用。因此当注释内容分为多行时, 通常用/ * … */ 方式; 如果用/ / 方式,则每行都要以/ / 开头。
  2. / * … */ 方式的注释不能嵌套, 但它可以嵌套/ / 方式的注释,例如:
 / * This is a multiline comment . 
 inside of which // is nested a single - line comment 
 Here is the end of the multiline comment . */

3.2 新的 I/ O 流

在 C 中进行 I/ O 操作时, 常会出现下面的错误:

int i; 
float f; 
scanf(% f”, i) ; 
printf(% d”, f) ;

在此,scanf( )和 printf( )所使用的格式控制符与输出数据的类型不一致, 但 C 编译器不能 检查出这些错误。scanf( )的第二个参数应是一个指针参数, 但这样的错误 C 编译器也不 能检查出来。
C + + 使用了更安全和更方便的方法来进行 I/ O 操作,如果使用 C + + 的 I/ O 操作, 上面的程序段可以写为:

int i; 
float f; 
cin > > i; 
cout < < f;

这里的 cin 是标准的输入流, 在程序中用于代表标准输入设备, 即键盘。运算符“ > > ”在 C + + 中仍保持 C 中的“右移”功能, 但用于输入时扩充了其功能, 表示将从标准输入流 (即键盘) 读取的数值传送给右方指定的变量。请看下面的语句:
cin > > x;
此时,用户从键盘输入的数值会自动地转换为变量 x 的类型, 并存入变量 x 内。x 必须是 基本数据类型,而不能是 void 类型。

运算符“ > > ”允许用户连续输入一连串数据, 例如:
cin > > a > > b > > c;
它按书写顺序从键盘上提取所要求的数据, 并存入对应的变量中。两个数据间用空白符 (空格、回车或 Tab 键 )分隔。


cout 是标准输出流, 在程序中用于代表标准输出设备, 通常指屏幕。运算符“ < < ”在 C + + 中仍保持 C 中的“左移”操作, 但用于输出时扩充了其功能, 表示将右方变量的值写 到标准输出流 cout 中,即显示在屏幕上。例如执行下面的语句后: **cout < < y;** 变量 y 的值将显示在屏幕上。y 必须是基本数据类型,而不能是 void 类型。 运算符“ < < ”允许用户连续输出一连串数据, 也可以输出表达式的值,例如: **cout < < a + b < < c;** 它按书写顺序将数据输出到屏幕上。

说明:

  1. 使用 cincout 进行 I/ O 操作时, 在程序中必须嵌入头文件 iostream .h, 否则编 译时要产生错误。下面是一个完整的 C + + 程序。
# incluade < iostream .h > 
int main ( ) 
{ 
	char name[ 20 ] ; 
	cout < < ”Hello, your name:; 
	cin > > name; 
	cout < < name; 
	return 0; 
}
  1. 在 C + + 程序 中, 我们仍 然可以沿 用传统的 stdio 函 数库 中的 I/ O 函数, 如 **printf( )**函数、**scanf( )**函数或其它的 C 输入输出函数, 但只有使用“cin > > ”和“cout < < ”才 能显示 C + + 的输入和输出风格。
  2. 使用“cin > >”可以连续输入多个数据,但由于用户常常忘记用空白符 (一个空格、 一个回车或一个 Tab 键 )来分隔两个数值,容易造成输入混乱, 因此使用时应加以注意。
  3. 前面用 coutcin 输出输入数据时, 全部使用了系统缺省的格式。实际上,我们 也可以对输入和输出格式进行控制。例如我们可用不同进制的方式显示数据, 这时就要 用设置转换基数的操纵符 dec、hex 和 oct。其中 dec 把转换基数设置为十进制, hex 把转 换基数设置为十六进制, oct 把转换基数设置为八进制, 缺省的转换基数是十进制。请看 下面的例子
# include < iostream .h > 
void main( ) 
{
	int x = 25; 
	cout < < hex < < x < <′′< < dec < < x < <′′< < oct < < x < <′\ n′; 
}

此例执行的结果为:

19 25 31

分别代表十六进制的 25、十进制的 25 及八进制的 25。

  1. 在 C 中, 常用′\ n′实现换行, C + + 中增加了换行控制符 endl, 其作用与′\ n′一 样。例如以下两个语句的操作是等价的:
cout < < ”x =< < x < < endl;
cout < < ”x =< < x < <′\ n′;

3.3 灵活的局部变量说明

在 C 语言程序中, 局部变量说明必须置于可执行代码段之前, 不允许局部变量说明 和可执行代码混合起来。例如在 C 中,下面的程序段是不正确的:

f( ) {int i; i = 10; int j; j = 25; / /}

因为其中语句 i = 10 插在两个变量说明之间, C 编译指示有错, 并中止对函数的编译。但 在 C + + 中,以上程序段是正确的, 编译时不会出错。
此外, C + + 中允许在 for 循环语句中说明变量, 如:

for (int k = 5 ; k > = 0; k - - )

可见, C + + 允许在代码块中的任何地方说明局部变量, 它所说明的变量从其说明点 到该变量所在的最小分程序末的范围内有效
通常认为:在大函数中, 在最靠近使用变量的位置说明变量较为合理; 而在较短的函 数中,把局部变量集中在函数开始处说明较好。

3.4 结构、联合和枚举名可直接作为类型名

在 C + + 中,结构名、联合名、枚举名都是类型名。在定义变量时, 不必在结构名、联 合名或枚举名前冠以 struct、unionenum, 例如:

enum bool{FALSE, TRUE} ; 
struct string{ 
	char * ptr; int length; 
} ;

在定义变量时,可以说明为:

bool done; 
string str; 

但是在传统的 C 语言中,必须写成:

 enum bool done; 
struct string str;

3.5 const 修饰符

在 C 中,习惯使用 # define 来定义常量, 例如:
# define LIMIT 100 ;
C + + 提供了一种更灵活、更安全的方式来定义常量, 即使用 const 修饰符来定义常 量,例如:
const int LIMIT = 100;
这个常量是类型化的,它有地址, 可以用指针指向这个值,但不能修改它。
const 也可以与指针一起使用,它们的组合情况较复杂, 可归纳为三种:

  • 指向常量的指 针
  • 常指针
  • 指向常量的常指针

  1. 指向常量的指针是指一个指向常量的指针变量, 例如:
    const char * name = ”chen”; / / 声明指向常量的指针
    这个语句的含义为: 声明一个名为 name 的指针变量, 它指向一个字符型常量, 初始化 name 为指向字符串”chen”。
    由于使用了 const ,不允许改变指针所指的常量, 因此以下语句是错误的:
    name[ 3] = ′a′;
    但是,由于 name 是一个指向常量的普通指针变量, 不是常指针, 因此可以改变 name 的 值。例如下列语句是允许的:
    name = ”zhang”;
    该语句赋给了指针另一个常量,即改变了 name 的值。
  2. 常指针是指把指针本身, 而不是它指向的对象声明为常量,例如:
    char * const name = ”chen”; / / 常指针
    这个语句的含义为: 声明一个名为 name 的指针变量, 该指针是指向字符型数据的常指 针,用”chen”的地址初始化该常指针。 创建一个常指针,就是创建一个不能移动的固定指针, 但是它所指的数据可以改变, 例如:
    name[ 3] = ′a′; / / 合法
    name = ”zhang”; / / 出错

    第一个语句改变了常指针所指的数据,这是允许的; 但第二个语句要改变指针本身, 这是 不允许的。
  3. 指向常量的常指针是指这个指针本身不能改变, 它所指向的值也不能改变。要 声明一个指向常量的常指针,二者都要声明为 const, 例如:
    const char * const name = ”chen”; / / 指向常量的常指针
    这个语句的含义是:声明了一个名为 name 的指针变量,它是一个指向字符型常量的常指 针,用“chen”的地址初始化该指针。不难理解以下二个语句都是错误的:
    name[ 3] =′a′; / / 出错, 不能改变指针所指的值 )
    name = ”zhang”; / / 出错, 不能改变指针本身


说明:

  1. 如果用 const 定义的是一个整型常量, 关键字 int 可以省略。所以下面的两行定 义是等价的: const int LIMIT = 100; const LIMIT = 100;
  2. 常量一旦被建立, 在程序的任何地方都不能再更改。
  3. 与 # define 定义的常量有所不同, const 定义的常量可以有自己的数据类型,这样 C + + 的编译程序可以进行更加严格的类型检查, 具有良好的编译时的检测性。
  4. const 的作用与 # define 相似,但它消除了 # define 的不安全性, 因此建议用const 取代 # define 定义常量。
  5. 函数参数也可以用 const 说明, 用于保证实参在该函数内部不被改动, 大多数 C + + 编辑器能对具有 const 参数的函数进行更好的代码优化。
  6. 虽然美国国家标准化协会 (ANSI) 制定的 C 标准 ( 简称 ANSI C 标准 ) 也采用了 const ,但两者略有差别, 如下面的程序段在 C + + 中是合法的, 但却不能被 ANSI C 所 接受
    const int size = 300;
    int a[size] ;

    另外,在 ANSI C 标准中, const 定义的常量是全局常量, 而 C + + 中 const 定义的常量 根据其定义的位置来决定其是局部的还是全局的。

3.6 内置函数

在函数说明前,冠以关键字“inline”, 该函数就被声明为内置函数。每当程序中出现 对该函数的调用时, C + + 编译器使用函数体中的代码替代函数调用表达式, 这样能加快 代码的执行,减少调用开销。下面的程序定义了一个内置函数。
说明:

  1. 内置函数在被调用之前必须进行完整的定义, 否则编译器将无法知道应该插入 什么代码。内置函数通常写在主函数的前面。
  2. C + + 的内置函数具有与 C 中的宏定义 # define 相同的作用和相似的机理,但消 除了 # define 的不安全因素。
  3. 若内置函数较长,且调用太频繁时, 程序将加长很多。因此, 通常只有较短的函 数才定义为内置函数,对于较长的函数, 最好作为一般函数处理。

3.7 函数原型

C 语言建议编程者为程序中的每一个函数建立原型,而 C + + 要求必须为每一个函 数建立原型,以说明函数的名称、参数类型与个数, 以及函数返回值的类型。其主要目的 是让 C + + 编译程序进行检查以确定调用函数的参数与事先定义的原型是否相符, 以及 返回值是否与原型相符,以维护程序的正确性。
函数原型的语法形式一般为:

返回类型 函数名(参数表) ;

函数原型是一条语句,它必须以分号结束。它由函数的返回类型、函数名和参数表构 成。参数表包含所有参数及它们的类型,参数之间用逗号分开。请看下面的例子。

# include < iostream .h > 
void write( char * s) ; // 函数原型的说明 
int main( ) 
{ 
	write(”Hello, world !) ; 
} 
void write( char * s) 
{
	 cont < < s; 
 }

在程序中,要求一个函数的原型出现在该函数的调用语句之前。这样, 当一个函数的 定义在后,而对它的调用在前时, 必须将该函数的原型放在调用语句之前; 但当一个函数 的定义在前,而对它的调用在后时, 一般就不必再单独给出它的原型了。因为, 这时函数 定义式的首部就起到了函数原型的声明作用。


说明:

  1. 函数原型的参数表中可不包含参数的名字, 而只包含它们的类型。例如以下的 函数原型是完全合法的:
    long Area( int, int) ;
    该原型声明一个返回类型为 long、有两个整型参数、名为 Area 的函数。尽管这一结构是 合法的,但是加上参数名将使原型更加清楚。例如, 带有参数名字的同一函数原型可以书 写成:
    long Area( int length, int width ) ;

  2. 函数定义由函数说明部分和函数体构成。函数说明部分与函数原型基本一样, 但函数说明部分中的参数必须给出名字,而且不包含结尾的分号, 例如:
    long Area( int length, int width ) { / / … return (length * width) ; }

  3. C + + 的参数说明必须放在函数名后的括号内,不可将函数参数说明放在函数说 明部分与函数体之间。因此编译以下的程序将会出现“style of function definition is now obsolete”的错误信息:

  4. 主函数 main( ) 不必进行原型说明, 因为它被看成一个自动说明原型的函数。主 函数是第一个被执行的函数,而且不存在被别的函数调用的问题。

  5. 原型说明中没有指出返回类型的函数( 包括主函数 main ) , C + + 默认该函数的 返回类型是 int, 因此以下的原型说明在 C + + 中是等价的:
    cal( float a, int c ) ; / / 默认返回整数
    int cal( float a, int c ) ; / / 指明返回整数

  6. 如果一个函数没有返回值, 则必须在函数原型中注明返回类型为 void。如果主 函数没有值可返回,可以在 main( ) 前注明 void,这样主函数中就不必有“return 0;”之类的 返回语句了。真正的 ANSI C 标准是不允许定义一个 void main( )的。

  7. 如果函数原型中未注明参数, C + + 假定该函数的参数表为空( void) 。例如以下 的原型说明在 C + + 中是完全一样的:
    f( ) ; / / 表示该函数不带任何参数
    f(void) ; / / 表示该函数不带任何参数

    但是在 C 中,上述两个原型说明是不同的:
    f(void) : / / 表示该函数不带任何参数
    f( ) ; / / 表示该函数的参数信息没有给出, 很可能它带有多个参数

3.8 带有缺省参数的函数

C + + 在说明函数原型时, 可为一个或多个参数指定缺省参数值, 以后调用此函数 时,若省略其中某一参数, C + + 自动地以缺省值作为相应参数的值。例如有一函数原型 说明为:
int special(int x = 5 ,float y = 5 .3) ;
则 x 与 y 的缺省参数值分别为 5 与 5 .3。

当进行函数调用时, 编译器按从左向右顺序将实参与形参结合, 若未指定足够的实 参,则编译器按顺序用函数原型中的缺省值来补足所缺少的实参。例如以下的函数调用 都是允许的:

special(100, 79 .8) ; // x = 100, y = 79.8 
special(25) ; // x = 25 , y = 5.3 
special( ) ; // x = 5, y = 5.3

说明:

  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. 在函数调用时, 若某个参数省略,则其后的参数皆应省略而采用缺省值。不允许 某个参数省略后,再给其后的参数指定参数值。例如不允许出现以下调用 special( )函数 的语句: special( , 21 .5 ) ;

3.9 函数重载

在传统的 C 语言中, 函数名必须是惟一的, 也就是说不允许出现同名的函数。当要 求编写求整数、浮点数和双精度数的平方数的函数时, 若用 C 语言来处理, 必须编写三个 函数,这三个函数的函数名不允许同名。例如:

Isquare(int i) ; // 求整数的二次方 
Fsquare( float i) ; // 求浮点数的二次方 
Dsquare( double i) ; // 求双精度数的二次方

当使用这些函数求某个数的平方数时,必须调用合适的函数, 也就是说, 用户必须记住三 个函数,虽然这三个函数的功能是相同的。
在 C + + 中,用户可以重载函数。这意味着, 只要函数参数的类型不同, 或者参数的 个数不同,或者二者兼而有之, 两个或者两个以上的函数可以使用相同的函数名。当两个 或者两个以上的函数共用一个函数名时,称为函数重载。被重载的函数称为重载函数。
由于 C + + 支持函数重载, 上面三个求二次方的函数可以起一个共同的名字 square, 但它们的参数类型仍保留不同。当用户调用这些函数时,只需在参数表中带入实参, 编译 器就会根据实参的类型来确定到底调用哪个重载函数。因此, 用户调用求二次方的函数 时,只需记住一个 square( )函数, 剩下的则都是系统的事情。

说明:

  1. 重载函数应在参数个数或参数类型上有所不同, 否则编译程序将无法确定调用 哪一个重载版本,即使返回类型不同, 也不能区分。例如:
    int mul(int x, int y) ;
    double mul( int x, int y) ;
    虽然这两个函数的返回类型不同,但参数个数和类型完全相同, 因此编译程序将无法区分 这两个函数。
  2. 一般而言, 重载函数应执行相同的功能, 例如 abs( ) 函数一般用来返回一个数的 绝对值,如果重载 abs( )函数, 让它返回一个数的二次方根,则是不可取的。

3.10 作用域运算符“∷”

通常情况下,如果有两个同名变量, 一个是全局的,另一个是局部的, 那么局部变量在 其作用域内具有较高的优先权。

#include < iostream .h > 
int avar = 10; // 全局变量 
int main ( ) { 
	int avar; // 局部变量 
	avar = 25; 
	cout < < ”avar is”< < avar < < endl; 
	return 0; 
}

程序执行结果如下: avar is 25
此时,在 main( ) 函数的输出语句中, 使用的变量 avar 是 main ( )函数内定义的局部变 量,因此打印的是局部变量 avar 的值。
如果希望在局部变量的作用域内使用同名的全局变量,可以在该变量前加上“∷”, 此 时∷avar 代表全局变量 avar“, ∷”称为作用域运算符。

# include < iostream .h > 
int avar; main ( ) 
{ 
	int avar; 
	avar = 25; / / 局部变量 
	avar ∷avar = 10; / / 全局变量 avar 
	cout < < ”local avar =< < avar < < endl; 
	cout < < ”global avar =< < ∷avar < < endl; 
	return 0; 
}

程序运行结果为: local avar = 25 global avar = 10

3.11 无名联合

无名联合是 C + + 中的一种特殊联合, 它在关键字 union 后面没有给出联合名, 它可 使一组数据项共享同一内存地址。如:

union {
	int i; 
	float f; 
}

在此无名联合中,声明了变量 i 和 f 具有相同的存储地址。无名联合可通过使用其中数据 项名字直接存取,例如可以直接使用上面的变量 i 或 f, 如:

i = 20 ;

3.12 强制类型转换

在 C 中如果要把一个整数(int)转换为浮点数 (float) ,要求使用如下的格式:

 int i = 10; 
 float x = (float) i;

C + + 支持这样的格式, 但还提供了一种更为方便的函数调用方法, 即将类型名作为函数 名使用,使得类型转换的执行看起来好像调用了一个函数。上面的语句可改写成:

  int i = 10;
   float x = float(i) ;

以上两种方法 C + + 都能接受,但推荐使用后一种方式。

3.13 new 和 delete

C 语 言使用函数 malloc ( ) 和 free ( ) 动态 分配内存和释放动态 分配的内 存。然 而 C + + 使用运算符 new 和 delete 能更好、更简单地进行内存的分配和释放。
运算符 new 用于内存分配的使用形式为:

p = new type;

其中, type 是一个数据类型名, p 是指向该数据类型的指针。new 从称为堆的一块自由存 储区中为程序分配一块 sizeof( type )字节大小的内存,该块内存的首地址被存于指针p 中。
运算符 delete 用于释放 new 分配的存储空间。它的使用形式一般为:

delete p;

其中, p 必须是一个指针, 保存着 new 分配的内存的首地址。下面是 new 和 delete 操作的 一个简单例子。

#include <iostream .h> 
int main() 
{
	int * p; // 声明一个整型指针变量 
	p E p = new int ; // 动态分配一个 int 型存储区,并将首地址赋给 p 
	*p = 10; 
	cout << *p; 
	delete p; // 撤消指针,释放 p 指向的存储空间 
	return 0; 
}

程序执行结果如下:

10

该程序定义了一个整型指针变量 p,然后用 new 为其分配了一块占两个字节的内存, p 指向这个内存块。然后在这内存块中赋予初值 10 ,并将其打印出来。最后撤消指针 p, 释放 p 指向的存储空间。

虽然 new 和 delete 完成的功能类似于 malloc ( ) 和 free( ) , 但是它们有以下几个优点:

  1. new 可以自动计算所要分配内存的类型的大小, 而不必使用 sizeof( ) 来计算所需 要的字节数,这就减少了发生错误的可能性。
  2. new 能够自动返回正确的指针类型,不必对返回指针进行类型转换。
  3. 可以用 new 将分配的对象初始化。
  4. new 和 delete 都可以被重载,允许建立自定义的分配系统。

下面我们对 new 和 delete 的使用再作几点说明:

  1. 使用 new 可以为数组动态分配内存空间,这时需要在类型名后面缀上数组大小。 例如:
int * pi = new int[10] ;

这时 new 为具有 10 个元素的整型数组分配了内存空间,并将首地址赋给了指针 pi。 使用 new 为多维数组分配空间时, 必须提供所有维的大小,如:

int * pi = new int[2 ] [ 3] [4 ] ;

其中第一维的界值可以是任何合法的表达式,如:

int i = 3; 
int * pi = new int[i] [3 ] [ 4] ;
  1. new 可在为简单变量分配内存空间的同时,进行初始化。初始值放在“new type” 后面的圆括号内。请看下面的例子。
# include < iostream .h > 
int main() 
{
	int * p; 
	p = new int( 99 ) ; // 动态分配内存,并将 99 作为初始值赋给它 
	cout << * p; 
	delete p; 
	return 0; }

但是, new 不能对动态分配的数组存储区进行初始化。

  1. 释放动态分配的数组存储区时, 可使用如下的 delete 格式:
delete [ ] p; 

无须指出空间大小,但老版本的 C + + 要求在 delete 的方括号中标出数字,以告诉 C + + 要释放多少个元素所占的空间。

  1. 使用 new 动态分配内存时,如果没有足够的内存满足分配要求, new 将返回空指 针( NULL) 。因此通常要对内存的动态分配是否成功进行检查。请看以下例子
# include < iostream .h > 
int main() 
{
	int * p; 
	p = new int ; 
	if( ! p) 
	{ 
		cout < < ”allocation failure \ n”; 
		return 1;
	 }
	 * p = 20; 
	  cout < < * p; 
	  delete p;
	  return 0; 
  }

若动态分配内存失败,此程序将在屏幕上显示“allocation failure”。

3.14 引用

引用是 C + + 中的新概念, 引用是能自动间接引用的一种指针。自动间接引用的意 思是:不必使用间接引用运算符 * , 就可以得到一个引用值。引用可为变量起别名, 它主 要用作函数参数以及函数的返回类型。

1 . 引用的定义
定义引用的关键字是“type &”, 它的含义是“type 类型的引用”, 此引用与 type 类型 的对象或变量的地址相联系。例如:

int i = 5; 
int &j = i;

它创建了一个整型引用, j 是 i 的别名, i 和 j 占用内存同一位置。当 i 变化时, j 也随之变 化,反之亦然。

  1. 定义引用时, 必须立即对它进行初始化,不能定义完成后再赋值。例如下述定义 是错误的:
nt i; 
int &j; // 错误 
j = i ;

为引用提供的初始值,可以是一个变量或另一个引用。例如:

int i = 5; 
int &j1 = i; 
int &j2 = j1 ;

这样定义后,变量 i 将有两个别名: j1 和 j2。

  1. 引用实际上是一种隐式指针。每次使用引用的变量时, 可以不用书写间接引用 运算符“ * ”,因而引用简化了程序。
  2. 引用不可重新赋值, 不可使其作为另一个变量的别名,例如:
int i, k; 
int &j = i; 
j = &k // 错误
  1. 引用不同于普通变量, 下面的类型声明都非法的:
nt &b[3 ] ; // 不能建立引用数组 
int & * p; // 不能建立指向引用的指针
int & & r; // 不能建立引用的引用
  1. 当使用 & 运算符取一个引用的地址时, 其值为所引用的变量的地址,例如:
int num = 50; 
int & ref = num; 
int * p = & ref;

则 p 中保存的是变量 num 的地址。
2 . 引用参数
C + + 提供引用, 其主要的一个用途就是将引用作为函数的参数。

#include<iostream.h> 
void swap(int * m, int * n) 
{
	int temp; 
	temp = * m; 
	* m = * n ;
	 * n = temp; 
}
int main () 
{
	int a = 5, b = 10; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	swap( & a, &b) ; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	return 0 ; 
}

程序运行的结果为:

a = 5 b = 10 
a = 10 b = 5

可见,采用按地址传递参数的方法, 调用函数 swap( )后, a 和 b 的值被交换了。
除了采用地址传递参数的方式外, C + + 又提供了采用“引用参数”传递函数参数的 方式,这是与上述方式性质完全不同的参数传递方式。

# include <iostream.h > 
void swap(int &m, int &n) 
{
	int temp; 
	temp = m;
	m = n; 
	n = temp; 
}
int main() 
{
	int a = 5, b = 10; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	swap( a , b) ; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	return 0 ; 
}

程序运行的结果为:

a = 5 b = 10 
a = 10 b = 5

当程序中调用函数 swap( )时,实参 a 和 b 分别初始化引用 m 和 n, 所在函数 swap ( ) 中, m 和 n 分别引用 a 和 b, 对 m 和 n 的访问就是对 a 和 b 的访问,所以函数 swap( )改变 了 main( )函数中变量 a 和 b 的值。

尽管通过引用参数产生的效果同按地址传递是一样的,但其语法更清楚简单, 因为在 调用函数的所有参数前不需要间接引用运算符 * ,原函数中传送的参数也不必是引用变 量。C + + 主张用引用传递取代地址传递的方式, 因为前者语法容易且不易出错。
3 . 引用返回值
函数可以返回一个引用,将函数说明为返回一个引用的主要目的是: 将该函数用在赋 值运算符的左边

# include < iostream .h > 
int a[ ] = {1, 3, 5, 7, 9} ; 
int &index(int) ; // 声明返回引用的函数
int main( ) 
{
	index (2 ) = 25; // 将 a [2 ]重新赋值为 25 
	cout << index(2 ) ; 
}
int &index(int i) 
{ 
	return a [i] ; 
}

程序运行结果为: 25
通常, 除了将函数定义为返回引用类型, 一个函数是不能直接用在赋值运算符左边 的。

在定义返回引用的函数时,注意不要返回对该函数内的自动变量( 局部变量)的引用。 例如:

int &fun()
{
	int a; // …
	return a; 
}

由于自动变量的生存期仅局限于函数内部, 当函数返回时, 自动变量就消失了,因此上述 函数返回一个无效的引用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章