第二章 C++基本知识速成
第一章已经从总体概念及基本原则等方面对面向对象程序设计进行了简要的介绍,之后我们将逐步推进,讨论面向对象编程(OOP)的实现。本章对C++语言的基本知识进行说明分析,帮助初学者尽快学会使用C++语言进行面向对象程序设计。
本章目的:
.介绍C++的词法符号(token)
.了解C++程序基本框架
.总览C++数据类型
.掌握C++运算符及表达式
.了解C++常量、变量的表示和用法
.掌握程序流程控制基本语句
2.1.1 C++的面向对象特征
概括地讲,面向对象系统包含3个要素:对象、类和继承。能支持所有上述3个方面的语言,被称为面向对象语言。从本质上讲,C++并不是一种完美的面向对象设计语言,而是C语言的一个超集,与C兼容也是其成功的一个重要方面,因而C++也是一种混合语言,既支持传统的面向过程程序设计,又支持面向对象程序设计,灵活性大。它继承了传统C语言的优点,克服了其不足;又在面向对象特征及非面向对象特征这两个方面增加了新功能,使得它既适用于结构化程序设计,又能满足面向对象程序设计的要求,符合广大程序设计逐步更新其程序设计观念和方法的要求,因而很快流行起来。
C++的面向对象特征和风格:
(1)C++借助于把一个数据结构及其操作函数组合在一起,提供了类/对象的构造和封装机制。
定义一个类的一般形式是:
class calss_name
{
private,//私有成员部分(类外部的代码不能直接访问的部分)
data and methods //可包括成员变量和成员函数
protected,//保护的成员部分
data and methods
public,//公用成员部分
data and methods //公有部分定义的变量和函数
}object_name_list;
在缺省关键词的情况下,类中所有项均为私有的。C++中的struct 和 union 也和 class一样能够定义一个类,所不同的是定义成员时,在缺省关键词的情况下所有成员均为公有的。
(2)提供对象的创建和删除,特别是使用了构造函数和析构函数来管理对象。
构造函数是一个特殊的成员函数,其函数命名与类名同名,它指定了一个类的实例对象的建立和初始化过程。
析构函数是用于释放内存和删除由构造函数创建的对象。
例:EX1_1.CPP
#include <iostream.h> //不同于C的stdio.h
class myclass
{
int a;
public:
myclass();
void show();
};
myclass::myclass()
{
cout<<”in constructor\n”; //cout:用于处理标准输出,
//它将输出流中的数据发送到标准输出设备(显示器)上。
//<<:将数据插入到输出流中。
a=10; //a的值由myclass()自动初始化
} //注意,构造函数是没有返回值的
void myclass::show()
{
cout<<a<<endl;
}
main()
{
myclass ob ; //变量说明语句也是动作语句,调用了构造函数
ob.show();
return 0;
}
其中:COUT为标准输出流〈〈
(3)能够进行对象间的消息传递、操作调用,除了使用操作符、指针、表达式外,还有指向自身的this等。
(4)支持继承机制,特别是提供了在一种通用的功能上进行具体化定义的方法,使用基类和派生类,形成了十分方便的继承机制。
在C++中,一个派生类可以有多个基类,支持多重继承。说明一个类继承另一个类的形式为:
class 派生类名:[public/private/protected] 基类名
{ //冒号是类属范围算符(双目)
.
.
,
}
例:EX1_2.CPP
class base
{
int x;
public:
void setx(int n){x=n;}
void showx(){cout<<x<<”\n”;}
};
class derived:public base
{
int y;
public:
void sety(int n){y=n;}
void showy(){cout<<y<<”\n”;}
};
main()
{
derived ob;
ob.setx(10);
ob.sety(20);
ob.showx();
ob.showy();
return 0;
}
(5)具有灵活的处理动态连接功能,其方法是使用虚函数(在函数名前用关键字virtual进行标识),它只能在最高层的基类中定义。
C++中函数名和运算符重载也是支持多态性的好方法。例如,下面重载构造函数提供了便利的初始化对象。
例:EX1_3.CPP
#include <iostream.h>
class myclass
{
int x;
public,//两种构造函数重载,使对象oba初始化,而obb没有
myclass(){x=0;} //no initializer
myclass(int n){x=n;} //initializer
int getx(){return x;}
};
main()
{
myclass oba(10); //declare with initial value
myclass obb; //declare without initializer
cout<<”oba:”<<oba.getx()<<”\n”;
cout<<”obb”<<obb.getx()<<”\n”;
return 0;
}
C++程序由函数和对象类组成,类在整体上代表一组对象的特征,声明类的目的上为了建立对象。对象是在运行时存在的实体(例),由类模板创建所需的对象时,一个特殊的成员函数(构造函数)被自动执行,以初始化建立的对象。
类代表由抽象分析所认识到的真实世界的概念对象,是语言中表达抽象的一个有力的工具。
2.1.2 C++语言对C语言在非面向对象方面的增强
C++除了继承传统C语言中的精华,并增加了许多的面向对象方面的特征之外,还在非面向对象方面对C语言进行了增强。
++提供了单行注释方式传统的C语言中提供了块注释方式,其形式如下:
/*explanation setence
*/
而在C++中,除保留块注释外,还增加了一种更方便的单行注释方式,其形式如下:
//explanation sentence
2、更为灵活的变量说明传统的C语言中,局部变量的说明必须放在可执行代码的前面。数据说明语句和可执行语句的混合将引起编译错误。而在C++中,可以在程序代码块的任何地方进行局部变量的说明,所说明变量的作用域是从对该变量进行说明的地方到该变量所在的最小分程序的未尾。
3、结构、联合和枚举名可直接作为类型名使用在C语言中,在对结构、联合和枚举类型进行定义后,要生成该结构、联合和枚举类型的变量,必须使用如下形式:
struct struct_name obj_name;
union union_name obj_name;
emun emun_name obj_name;
即必须在说明的结构、联合和枚举名的前面加上说明其所属类型的标识符。而在C++中,在对结构、联合和枚举类型进行定义后,该结构、联合和枚举名便可作为类型名使用,可以直接用它进行对象定义。
4、枚举类型与整数关系在C语言中,枚举值与整数值之间可以相互转换使用。而C++的类型检查则比C要严格,枚举值将被自动转化为整型值使用,但将整型值转化为枚举值则需要进行强制类型转换,否则会出现编译错误。如下面的一段代码,在C语言中都是可以接受的,但某些语句在C++中则会引起错误。
emun name {John,Mike,Rose,Joan};
emun name person1=John;
emun name person2=person1;
emun name person3=1; //This is an error in C++!
emun name person4=(emun name)1;
5、C++增加了无名联合类型无名联合是C++中提供的一种特殊联合,可以用来说明一组无标记名的、共享同一内存地址的数据项。例如:
union
{
int i;
float f;
};
无名联合可以通过使用其中数据项名字直接进行访问。(?)
6、作用域限定运算符::
作用域限定运算符::用于对当前作用域之外的同名变量进行访问。如在下面的例子中,在局部变量var的作用域内使用::var来实现对全局变量var的访问。
例:EX1_4.CPP
#include <iostream.h>
int var;
void main()
{
float var;
var=3.14; //局部变量var
::var=6; //全局变量var
cout<<”local variable var=”<<var<<”\n”;
cout<<”globle variable var=”<<::var<<”\n”;
}
7、函数调用时的参数传递方式在C语言中,函数调用时的参数传递是用按值传递方式进行的,在子函数中对形参的改变并不引起实参的数值改变。而在C++中,函数调用时的参数传递是用按地址传递方式进行的,形参和实参所指向的是同一内存区域的内容,因此在了函数中对形参的修改将会导致实参的数值变化。
8、增强了类型转换方式在C++中,除保留了传统C语言的强制类型转换方式外,还可以将类型名作为函数名使用,这将大大提高程序的可读性。例如:
float f=0.01;
long l=(long)f;
long k=long(f); //将类型名作为函数名用
9、void类型在C++中,void类型指针是可以与任何类型指针相匹配的指针。Void类型指针可以被赋给任何类型的指针,从而可以避免许多无用的类型转换。而void型函数是无返回值的函数,它可以限制函数返回一个无用的int型数值。
10、关键字const
在C++中,关键字const用于将一个标识符说明为常量,程序不能以任何方式对其进行修改。而C++中的const与C语言中#dedfine有区别:const所说明的常量是有内存单元与之对应的量,在程序编译时其值还不能确定;而#define所说明的是在编译时便能确定其数值的常量。
关键字const也可以用来修饰函数参数表中的某些参数,用以保证被修饰的参数对应的实参在该函数内部不被改动。
当关键字const用来修饰指针时,根据使用的形式不同,它可以冻结指针所指向的变量、冻结指针本身或同时冻结指针用其所指向的变量,如下所示:
const char *name=”Mike”; //冻结指针所指向的变量
char *const name=”Mike”; //冻结指针本身
const char *const name=”Mike”; //同时冻结指针及其所指向的变量
11、类型兼容性
C++的类型匹配检查比C更为严格。在C中长度同为1字节的char、unsigned char、signed char型变量在相互赋值时可以不进行强制类型转换,而在C++中,则必须进行强制类型转换,否则会出现编译错误。在类型兼容性方面的增强将大大提高程序的可移植性。
12、字符常量的大小在C语言中,字符常量是按照整型进行处理的,它们的长度都是2B。而在C++中,整型变量的长度仍为2B,但字符常量的长度只有1B,这便得C++程序占用内存空间更为节约,程序运行效率更高。
13、new和delete
C++为了提高内存管理上的灵活性,提供了动态内存分配和释放的操作符new和delete,用来(代替)增强C语言中原有的函数malloc()和free()。
14、函数原型在C++中,函数原型说明要求给出该函数的参数类型,这比传统的C语言的函数原型说明更有利于编译系统检查非法参数调用,可以避免出现一些难于发现和调试的错误。
15、内联函数(inline)
C++提供了内联函数。使用内联函数可以节约函数调用时保留现场所需的系统开销,提高程序执行效率。同时,由于内联函数的使用将增大了程序的代码段,因此建议在使用时充分权衡系统开销和程序代码段长度的关系,要谨慎使用。
16、缺省函数参数在C++的函数调用时,可以缺省函数的参数。但是要使用函数参数,必须在函数定义时为该参数赋一缺省值,且所缺省的参数的顺序只能是从左到右,不能随意缺省。例如:
int function(int a=0;int b=1;int c=2;)
{
.
.
.
}
以下函数调用都是合法的:
function(); //equal to function(0,1,2)
function(12); //equal to function(12,1,2)
function(12,13); //equal to function(12,13,2)
而以下函数调用则是非法的:
function(,12,13)
function(12,,13)
17、函数返回值在C++中,任何说明为非void类型的函数都需要指定一个相应类型的返回值,否则在程序的编译时会产生警告错误。
第一章已经从总体概念及基本原则等方面对面向对象程序设计进行了简要的介绍,之后我们将逐步推进,讨论面向对象编程(OOP)的实现。本章对C++语言的基本知识进行说明分析,帮助初学者尽快学会使用C++语言进行面向对象程序设计。
本章目的:
.介绍C++的词法符号(token)
.了解C++程序基本框架
.总览C++数据类型
.掌握C++运算符及表达式
.了解C++常量、变量的表示和用法
.掌握程序流程控制基本语句
2.1.1 C++的面向对象特征
概括地讲,面向对象系统包含3个要素:对象、类和继承。能支持所有上述3个方面的语言,被称为面向对象语言。从本质上讲,C++并不是一种完美的面向对象设计语言,而是C语言的一个超集,与C兼容也是其成功的一个重要方面,因而C++也是一种混合语言,既支持传统的面向过程程序设计,又支持面向对象程序设计,灵活性大。它继承了传统C语言的优点,克服了其不足;又在面向对象特征及非面向对象特征这两个方面增加了新功能,使得它既适用于结构化程序设计,又能满足面向对象程序设计的要求,符合广大程序设计逐步更新其程序设计观念和方法的要求,因而很快流行起来。
C++的面向对象特征和风格:
(1)C++借助于把一个数据结构及其操作函数组合在一起,提供了类/对象的构造和封装机制。
定义一个类的一般形式是:
class calss_name
{
private,//私有成员部分(类外部的代码不能直接访问的部分)
data and methods //可包括成员变量和成员函数
protected,//保护的成员部分
data and methods
public,//公用成员部分
data and methods //公有部分定义的变量和函数
}object_name_list;
在缺省关键词的情况下,类中所有项均为私有的。C++中的struct 和 union 也和 class一样能够定义一个类,所不同的是定义成员时,在缺省关键词的情况下所有成员均为公有的。
(2)提供对象的创建和删除,特别是使用了构造函数和析构函数来管理对象。
构造函数是一个特殊的成员函数,其函数命名与类名同名,它指定了一个类的实例对象的建立和初始化过程。
析构函数是用于释放内存和删除由构造函数创建的对象。
例:EX1_1.CPP
#include <iostream.h> //不同于C的stdio.h
class myclass
{
int a;
public:
myclass();
void show();
};
myclass::myclass()
{
cout<<”in constructor\n”; //cout:用于处理标准输出,
//它将输出流中的数据发送到标准输出设备(显示器)上。
//<<:将数据插入到输出流中。
a=10; //a的值由myclass()自动初始化
} //注意,构造函数是没有返回值的
void myclass::show()
{
cout<<a<<endl;
}
main()
{
myclass ob ; //变量说明语句也是动作语句,调用了构造函数
ob.show();
return 0;
}
其中:COUT为标准输出流〈〈
(3)能够进行对象间的消息传递、操作调用,除了使用操作符、指针、表达式外,还有指向自身的this等。
(4)支持继承机制,特别是提供了在一种通用的功能上进行具体化定义的方法,使用基类和派生类,形成了十分方便的继承机制。
在C++中,一个派生类可以有多个基类,支持多重继承。说明一个类继承另一个类的形式为:
class 派生类名:[public/private/protected] 基类名
{ //冒号是类属范围算符(双目)
.
.
,
}
例:EX1_2.CPP
class base
{
int x;
public:
void setx(int n){x=n;}
void showx(){cout<<x<<”\n”;}
};
class derived:public base
{
int y;
public:
void sety(int n){y=n;}
void showy(){cout<<y<<”\n”;}
};
main()
{
derived ob;
ob.setx(10);
ob.sety(20);
ob.showx();
ob.showy();
return 0;
}
(5)具有灵活的处理动态连接功能,其方法是使用虚函数(在函数名前用关键字virtual进行标识),它只能在最高层的基类中定义。
C++中函数名和运算符重载也是支持多态性的好方法。例如,下面重载构造函数提供了便利的初始化对象。
例:EX1_3.CPP
#include <iostream.h>
class myclass
{
int x;
public,//两种构造函数重载,使对象oba初始化,而obb没有
myclass(){x=0;} //no initializer
myclass(int n){x=n;} //initializer
int getx(){return x;}
};
main()
{
myclass oba(10); //declare with initial value
myclass obb; //declare without initializer
cout<<”oba:”<<oba.getx()<<”\n”;
cout<<”obb”<<obb.getx()<<”\n”;
return 0;
}
C++程序由函数和对象类组成,类在整体上代表一组对象的特征,声明类的目的上为了建立对象。对象是在运行时存在的实体(例),由类模板创建所需的对象时,一个特殊的成员函数(构造函数)被自动执行,以初始化建立的对象。
类代表由抽象分析所认识到的真实世界的概念对象,是语言中表达抽象的一个有力的工具。
2.1.2 C++语言对C语言在非面向对象方面的增强
C++除了继承传统C语言中的精华,并增加了许多的面向对象方面的特征之外,还在非面向对象方面对C语言进行了增强。
++提供了单行注释方式传统的C语言中提供了块注释方式,其形式如下:
/*explanation setence
*/
而在C++中,除保留块注释外,还增加了一种更方便的单行注释方式,其形式如下:
//explanation sentence
2、更为灵活的变量说明传统的C语言中,局部变量的说明必须放在可执行代码的前面。数据说明语句和可执行语句的混合将引起编译错误。而在C++中,可以在程序代码块的任何地方进行局部变量的说明,所说明变量的作用域是从对该变量进行说明的地方到该变量所在的最小分程序的未尾。
3、结构、联合和枚举名可直接作为类型名使用在C语言中,在对结构、联合和枚举类型进行定义后,要生成该结构、联合和枚举类型的变量,必须使用如下形式:
struct struct_name obj_name;
union union_name obj_name;
emun emun_name obj_name;
即必须在说明的结构、联合和枚举名的前面加上说明其所属类型的标识符。而在C++中,在对结构、联合和枚举类型进行定义后,该结构、联合和枚举名便可作为类型名使用,可以直接用它进行对象定义。
4、枚举类型与整数关系在C语言中,枚举值与整数值之间可以相互转换使用。而C++的类型检查则比C要严格,枚举值将被自动转化为整型值使用,但将整型值转化为枚举值则需要进行强制类型转换,否则会出现编译错误。如下面的一段代码,在C语言中都是可以接受的,但某些语句在C++中则会引起错误。
emun name {John,Mike,Rose,Joan};
emun name person1=John;
emun name person2=person1;
emun name person3=1; //This is an error in C++!
emun name person4=(emun name)1;
5、C++增加了无名联合类型无名联合是C++中提供的一种特殊联合,可以用来说明一组无标记名的、共享同一内存地址的数据项。例如:
union
{
int i;
float f;
};
无名联合可以通过使用其中数据项名字直接进行访问。(?)
6、作用域限定运算符::
作用域限定运算符::用于对当前作用域之外的同名变量进行访问。如在下面的例子中,在局部变量var的作用域内使用::var来实现对全局变量var的访问。
例:EX1_4.CPP
#include <iostream.h>
int var;
void main()
{
float var;
var=3.14; //局部变量var
::var=6; //全局变量var
cout<<”local variable var=”<<var<<”\n”;
cout<<”globle variable var=”<<::var<<”\n”;
}
7、函数调用时的参数传递方式在C语言中,函数调用时的参数传递是用按值传递方式进行的,在子函数中对形参的改变并不引起实参的数值改变。而在C++中,函数调用时的参数传递是用按地址传递方式进行的,形参和实参所指向的是同一内存区域的内容,因此在了函数中对形参的修改将会导致实参的数值变化。
8、增强了类型转换方式在C++中,除保留了传统C语言的强制类型转换方式外,还可以将类型名作为函数名使用,这将大大提高程序的可读性。例如:
float f=0.01;
long l=(long)f;
long k=long(f); //将类型名作为函数名用
9、void类型在C++中,void类型指针是可以与任何类型指针相匹配的指针。Void类型指针可以被赋给任何类型的指针,从而可以避免许多无用的类型转换。而void型函数是无返回值的函数,它可以限制函数返回一个无用的int型数值。
10、关键字const
在C++中,关键字const用于将一个标识符说明为常量,程序不能以任何方式对其进行修改。而C++中的const与C语言中#dedfine有区别:const所说明的常量是有内存单元与之对应的量,在程序编译时其值还不能确定;而#define所说明的是在编译时便能确定其数值的常量。
关键字const也可以用来修饰函数参数表中的某些参数,用以保证被修饰的参数对应的实参在该函数内部不被改动。
当关键字const用来修饰指针时,根据使用的形式不同,它可以冻结指针所指向的变量、冻结指针本身或同时冻结指针用其所指向的变量,如下所示:
const char *name=”Mike”; //冻结指针所指向的变量
char *const name=”Mike”; //冻结指针本身
const char *const name=”Mike”; //同时冻结指针及其所指向的变量
11、类型兼容性
C++的类型匹配检查比C更为严格。在C中长度同为1字节的char、unsigned char、signed char型变量在相互赋值时可以不进行强制类型转换,而在C++中,则必须进行强制类型转换,否则会出现编译错误。在类型兼容性方面的增强将大大提高程序的可移植性。
12、字符常量的大小在C语言中,字符常量是按照整型进行处理的,它们的长度都是2B。而在C++中,整型变量的长度仍为2B,但字符常量的长度只有1B,这便得C++程序占用内存空间更为节约,程序运行效率更高。
13、new和delete
C++为了提高内存管理上的灵活性,提供了动态内存分配和释放的操作符new和delete,用来(代替)增强C语言中原有的函数malloc()和free()。
14、函数原型在C++中,函数原型说明要求给出该函数的参数类型,这比传统的C语言的函数原型说明更有利于编译系统检查非法参数调用,可以避免出现一些难于发现和调试的错误。
15、内联函数(inline)
C++提供了内联函数。使用内联函数可以节约函数调用时保留现场所需的系统开销,提高程序执行效率。同时,由于内联函数的使用将增大了程序的代码段,因此建议在使用时充分权衡系统开销和程序代码段长度的关系,要谨慎使用。
16、缺省函数参数在C++的函数调用时,可以缺省函数的参数。但是要使用函数参数,必须在函数定义时为该参数赋一缺省值,且所缺省的参数的顺序只能是从左到右,不能随意缺省。例如:
int function(int a=0;int b=1;int c=2;)
{
.
.
.
}
以下函数调用都是合法的:
function(); //equal to function(0,1,2)
function(12); //equal to function(12,1,2)
function(12,13); //equal to function(12,13,2)
而以下函数调用则是非法的:
function(,12,13)
function(12,,13)
17、函数返回值在C++中,任何说明为非void类型的函数都需要指定一个相应类型的返回值,否则在程序的编译时会产生警告错误。