C++中提供了一套输入输出流方法的对象,它们是cin和cout,cerr,对应c语言中的三个文件指针stdin,stdout,stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。cin与>>一起完成输入操作,cout,cerr与<<一起完成输出与标准错误输出。例如程序main函数中使用cin为pt.x1,pt.y1输入两个整数,Output函数中使用cout连续输出字符串、整数、字符、换行。在输出中使用endl(end of line)表示换行,相当与'\n'。利用cin和cout比scanf和printf要方便得多,cin和cout可以自动判别输入输出数据类型而自动调整输入输出格式,不必象scanf和printf那样一个个由用户指定。使用cin,cout不断方便,而且减少了出错的可能性。
从类CPoint的Output成员函数的实现中,我们可以看到类中的成员函数可以直接访问同类中的成员变量,如:x1,y1,x2,y2。说明:如果成员函数中的局部变量与成员变量同名,则在局部变量的作用范围内,成员变量不起作用。如果有全局变量与成员变量同名,则在成员变量的作用范围内(所有同类成员函数中),全局变量不起作用。main函数中的if(1==1)语句部分,主要是为了说明局部变量的有效范围。局部变量的有效范围位于定义它的复合语句之中,一对{}中所定义的语句即一个复合语句。也就是说,局部变量的有效范围并不是在定义它的函数体当中,而是在外层最靠近它定义的那对{}中,main()函数中定义的第一个CPoint对象pt在if语句的}处被系统释放。
在类中使用的private和public访问修饰符,它们限定成员被访问的范围。从一个修饰符的定义处,直到下一个修饰符定义之间的所有成员都属于第一个修饰符所定义的访问类型。
以public定义的修饰符,能够被同类中的成员函数及类定义之外的所有其他函数访问,如CPoint类中的x1,y1,Output等成员变量与函数。但要注意在类之外的函数中访问类成员,必须是对象.成员的格式。
以private定义的成员,只能被同类中的成员函数中访问,不能在其他函数中访问(即使是对象.成员的格式),如类CPoint中的成员变量x2,y2能被成员函数Output访问,但不能在main函数及全局Output函数中访问。说明:如果在类定义中的开始处没有使用任何修饰符,则在类定义的开始处使用private作为其默认修饰符。在C++中定义struct结构体也可以包含成员函数,除了开始处使用的默认修饰符为public外,其余之处与class类完全相同。
二、函数的重载:
在C语言中,如果同一程序中有两个函数名一样,但参数类型或个数不一样的函数定义,编译时将会出错。如果程序中有两个名为Add的函数定义,如:
int Add(int x,int y);
int Add(int x,int y,int z);
在C语言中编译,编译将提示函数名重复错误。在C++中上述定义是合法的,C++能够根据函数调用时所传递的参数个数及数据类型选择适当的函数。
三、构造函数与析构函数:
在类的定义中,有一种特殊的函数,函数名称与类的名称相同,我们称之为构造函数。构造函数不能有返回类型。因为C++支持函数的重载,所以一个类中可以有多个不同参数形式的构造函数。当用类去定义一个变量(后面可以附带参数),也就是在内存中产生一个类的实例(用类定义的实例变量通常也叫对象)时,程序将根据参数自动调用该类中对应的构造函数。如程序中CPoint pt;语句中调用函数CPoint(),CPoint pt(10,10)语句调用函数CPoint(int x2,int y2)。
如果类中有一个函数定义格式为~类名(),如~CPoint(),这个函数就称为析构函数,同样析构函数也不允许有返回值,析构函数不允许带参数,并且一个类中只能有一个析构函数。析构函数的作用正好与构造函数相反,对象超出其作用范围,对应的内存空间被系统收回或被程序用delete删除时,析构函数被调用。
根据构造函数的这种特点,可以在构造函数中初始化对象的某些成员变量,也就是初始化实例对象。在析构函数中释放对象运行期间所申请的资源,如动态申请的内存空间。通俗地讲,构造函数的作用是,在对象产生时,自动为其赋初值;析构函数的作用是,在对象消失时,为对象处理后事。提示:在类中定义成员变量时,不能给这些变量赋初值。如:
class A
{
int x=0;//错误,此处不能给变量x赋值。
};
在类中定义了一个指向整数的指针成员变量pCount,它所指向的内存地址中的数据(一个整数大小空间)用于统计成员函数Output被调用的次数。提示:在例子中的pCount的用法在实际应用中并不合理,我们这么使用主要是为了分析问题。在c++动态申请内存,是new操作符完成的,new操作符申请的内存是从堆中分配的。在程序中定义的变量所用内存是从栈中分配的,当变量超出其作用范围时,系统收回该变量所占用的内存,以后再分配给其他变量使用。new操作符申请的内存空间在程序运行期间是不会被系统收回的,除非程序调用delete操作符明确要求释放该内存空间。
我们对程序中关于pCount的语句进行分析。int *pCount定义了一个变量pCount,pCount变量自身是系统从栈中分配的,占四个字节。这个过程同定义一个整数变量的过程(int x;)没有什么两样,其中的数据没有被初始化,是一个不确定的数,一般不等于零。与定义一个整数变量不一样的是,pCount中的数据是用来表示某一内存块的地址的。尽管系统已为pCount自身分配了内存空间,但pCount中的那个不确定的数据所对应的地址空间却是没有分配的,不可使用的,如图1所示。*pCount=1;表示将pCount所指向的内存块中数据置为1,如果pCount中的数据所指向的内存块是不存在的或未被分配的,程序将会出错。同样(*pCount)++;表示将pCount所指向的内存块中数据加1,delete pCount;表示释放
pCount所指向的内存块,这些操作都要求pCount中的数据所指向的内存块是被分配过的,合法的。
pCount=new int;通过new操作符在堆中分配了一个整数变量空间大小的内存块,并将该内存块的首地址赋值给pCount,pCount中的数据便指向了这一段合法的地址空间,如图2所示。如果程序超出了pCount的定义范围,pCount变量自身的内存空间将被系统收回,但不影响pCount中的数据所指向的内存块,如果该内存块是通过new分配的,必须保证该内存块不再被使用时用delete删除掉,否则将会造成内存泄漏。
我们如何确定pCount中的数据所指向的内存块是否被正常分配过的呢?我们一般通过检查pCount中的数据是否为零来判断的,这就需要我们在pCount被分配之后将其初始化为0。由于pCount是在类中定义的成员变量,不能在定义变量时为其赋值,所以必须在构造函数中将其初始化为0。这样,一旦用CPoint定义一个对象,该对象中的pCount成员将立即被初始化为0,这下读者应该明白构造函数的作用了吧!在Output成员函数中,检查pCount是否为0,如果为零,则用new int;为其分配一个整数变量大小空间,并将其地址赋值给pCount,否则,直接引用原来已分配的空间。当CPoint定义的对象超出其有效范围时,为该对象分配的空间将被释放,pCount变量也将随该对象一并被释放,如果pCount已指向一个用new操作符分配的内存空间,该内存空间不会被释放,所以,我们必须在析构函数中用delete操作符释放该内存空间,保证pCount被释放前也释放掉pCount中的数据所指向的内存空间,这下读者也应该明白析构造函数的作用了!
评论