可以使用友元成员函数。在本例中除了介绍有关友元成员函数的简单应用外,还将用到类的提前引用声明,请读者注意。
编写程序:
运行结果:
程序分析:
在一般情况下,两个不同的类是互不相干的。display函数是Time类中的成员函数,它本来只可以用来输出Time类对象中的数据成员hour,minute,sec。现在在Date类中把它声明为"朋友",因此也可以访问Date类对象中的数据成员mouth,day,year。所以在display函数中既可以输出Time类的时、分、秒,又可以输出其"朋友"类的对象中的年、月、日。注意,在输出本类对象的时、分、秒时,不必使用对象名,而在输出Date类的对象中的年、月、日时,就必须加上对象名(如d.month)。如果不用友元函数,为了实现题目要求,就要在两个类中分别包括两个输出函数(如display1,display2),在主函数中分别调用这两个函数,先后输出日期和时间。显然用友元函数方便。
请注意在本程序中调用友元函数访问有关类的私有数据方法:
(1)在函数名display的前面要加display所在的对象名(如t1)。
(2)display 成员函数的实参是Date类对象d1,否则就不能访问对象d1中的私有数据。
(3)在Time::display函数中引用Date类私有数据时必须加上对象名,如d.month。
注意:在本例中声明了两个类Time和Date。程序第3行是对Date类的声明,因为在第7行和第16行中对display函数的声明和定义中要用到类名Date,而对Data类的定义却在其后面。能否将Date类的声明提到前面来呢?也不行,因为在 Date类中第 4行又用到了Time类,也要求先声明Time类才能使用它。这就形成了"连环套",类似于"鸡生蛋,蛋生鸡"的问题。为了解决这个问题,C++允许对类进行"提前引用"的声明,即在正式声明一个类之前,先声明一个类名,表示此类将在稍后声明。程序第3行就是提前引用声明,它只包含类名,不包括类体。如果没有第3行,程序编译就会出错。有了第3行,在编译时,编译系统会从中得知 Date是一个类名,此类将在稍后定义。
有关对象提前引用的知识:在一般情况下,对象必须先声明,然后才能使用它。但是在特殊情况下(如本例所示的这样),在正式声明类之前,需要使用该类名。但是应当注意:类的提前声明的使用范围是有限的。只有在正式声明一个类以后才能用它去定义类对象。如果在上面程序第3行后面增加一行:
Date d1; //试图定义一个对象
会在编译时出错。因为在定义对象时是要为这些对象分配存储空间的,在正式声明类之前,编译系统无法确定应为对象分配多大的空间。编译系统只有在"见到"类体后,才能确定应该为对象预留多大的空间。在对一个类做了提前引用声明后,可以用该类的名字去定义指向该类型对象的指针变量或对象的引用(如在本例中,display的形参是Date类对象的引用)。这是因为指针变量和引用与它所指向的类对象的大小无关。
请注意程序是在定义Time::display函数之前正式声明Date类的。如果将对Date类的声明的位置(程序第13~21行)改到定义Time::display函数之后,编译就会出错,因为在Time::display函数体中要用到Date类的成员month,day,year。如果不事先声明Date类,编译系统无法识别成员 month,day,year等成员。读者可以上机调试一下。
说明: 一个函数( 包据普通函数和成员函数)可以被多个类声明为"朋友",这样就可以引用多个类中的私有数据。