ITEEDU

6.10 初始化类对象:构造函数

生成类对象时,其成员可以用类的构造函数初始化。构造函数是与类同名的成员函数。程序员提供的构造函数在每次生成类对象(实例化)时自动调用。构造函数可以重载.提供初始化类对象的不同方法。数据成员应在类的构造函数中初始化或在生成对象之后设置其数值。

常见编程错误6. 7

类的数据成员只能在类定义中初始化。

常见编程错误6.8

试图声明构造函数的返回类型和返回植是个语法错误。

编程技巧6.5

适当时候(通常都是)应提供一十构速函数,保证每个对象正确地初始化为有意义的值。特别是指针数据类型应初始化为合法指针值或0。

测试与调试提示6.4

每个修改对象的private数据成员的成员函数(和友元)应确保数据保持一致状态。

声明类对象时,可以在括号中提供初始化值,放在对象名后面和分号前面。这些初始化值作为

参数传递给类的构造函数。稍后会举几个构造函数调用(constructor call)的例子(注意:尽管程序

员不显式调用构造函数,但程序员仍然可以提供数据,作为参数传递给构造函数)。

6.11 在构造函数中使用默认参数

图6.1time1.cpp中的构造函数将hour、minute和second初始化为0(即军用时间午夜11时)。

构造函数可以包含默认参数。图6.8重新定义Time的构造函数,该函数中每个变量的默认参数为0。通过提供构造函数默认参数,即使在构造函数调用中不提供数值,对象也能利用默认参数初始化为一致状态。程序员提供所有参数默认值(或显式不要求参数)的构造函数也称为默认构造函数(default constnlctor),即可以不用参数而调用的构造函数。一个类只能有一个默认构造函数。

// Fig. 6.8: time2.h
 // Declaration of the Time class.
 // Member functions are defined in time2.cpp
 // preprocessor directives that
 // prevent multiple inclusions of header file
 #ifndef TIME2_H
 #define TIME2_H
 // Time abstract data type definition
 class Time {
 public:
   Time( int = 0, int = 0, int = 0 );  // default constructor
   void setTime( int, int, int ); // set hour, minute, second
   void printMilitary();        // print military time format
   void printStandard();        // print standard time format
 private:
   int hour;    // 0 - 23
   int minute;  // 0  59
   int second;  // 0 - 59
 };
 #endif
 // Fig. 6.8: time2.cpp
 // Member function definitions for Time class.
 #include <iostream.h>
 #include "time2.h"
 // Time constructor initializes each data member to zero.
 // Ensures all Time objects start in a consistent state.
 Time::Time(int hr,int min,int sec)
   { setTime( hr, min, sec ); }
 // Set a new Time value using military time. Perform validity
 // checks on the data values. Set invalid values to zero.
 void Time::setTime(int h,int m,int s)
 {
   hour = ( h >= 0 && h < 24 ) ? h : 0;
   minute = ( m >0  && m < 60 )?  m : 0;
   second = ( s >= 0 && s < 60 ) ? s : 0;
 }
 // Print Time in military format
 void Time::printMilitary()
   cout << ( hour < 10 ? "O" :  "" ) << hour << ":"
        << ( minute < 10 ? "0" :  "" ) << minute;
 }
 // Print Time in standard format
 void Time::printStandard()
   cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 )
        << ":" <<(  minute < 10 ? "0" :  "" ) << minute
        << ":" << ( second < 10 ? "0" :  "" ) << second
        << ( hour < 12 ? "AM" : "PM" );
 }
 // Fig. 6.8: fig06_08.cpp
 // Demonstrating a default constructor
 // function for class Time.
 #include< iostream.h>
 #include "time2.h"
 int main()
 {
    Time t1,           // all arguments defaulted
   t2(2),        // minute and second defaulted
   t3(21, 34),    // second defaulted
   t4(12, 25, 42), // all values specified
   t5(27, 74, 99); // all bad values specified
   cout << "Constructed with:\n"
        << "all arguments defaulted:\n  ";
   t1.printMilitary();
   cout << "\n  ";
   t1.printStandard();
   cout << "\nhour specified; minute and second defaulted:"
        << "\n  ";
   t2.printMilitary{);
   cout << "\n  ";
   t2.printStandard();
   cout << "\nhour and minute specified; second defaulted:"
        << "\n  ";
   t3.printMilitary();
   cout << "\n ";
   t3.printStandard();
   cout << "\nhour, minute, and second specified:"
        << "\n  ";
   t4.printMilitaryO;
   cout << "\n  ";
   t4.printStandard();
   cout << "\nall invalid values specified:"
        << "\n  ";
   t5.printMilitary();
   cout << "\n  ";
 t5.printStandardO;
  cout << endl;
   return 0;
 }

输出结果:

Constructed with:

all arguments defaulted

00:00

12:00:00 AM

hour specified; minute and second defaulted:

02:00

2:O0:0O AM

hour and minute specified; second defaulted:

21:34

9:34:00 PM

hour, minute, and second specified:

12:25

12:25:42 PM

all invalid values specified:

00:00

12:00:00 AM

图 6.8 构造函数使用默认参数

在这个程序中,构造函数调用成员函数setTime,将数值传人构造函数(或用默认值)保证hour 值取0到13、minute和second值取0到59。如果数值超界,则setTime将其设置为0(使数据成员保证一致状态)。

注意,Time构造函数也可以写成包含与setTime成员函数相同的语句。这样可能会使程序更有效,因为不必另外再调用setTime函数。但让Time构造函数和setTime成员函数使用相同代码会使程序维护更加困难。如果setTime成员函数的实现方法改变,则Time构造函数的实现方法也要相应 改变。Time构造函数直接调用setTime时,setTime成员函数的实现方法只要改变一次即可,这样就 可以在改变实现方法时减少错误。另外,显式声明内联的构造函数或在类定义中定义构造函数也可以提高Time构造函数的性能(后者隐含内联函数定义)。

软件工程视点6.19

如果类的成员函数已经提供类的构造函数(或其他成员函数)所需功能的所有部分,则从构造函数(或其他成员函数)调用这个成员函数。这样可以简化代码维护和减少修改代码实现方法时的出错机会。因此就形成了一个一般原则:避免重复代码。

编程技巧6.6

只在头文件内的类定义的函数原型中声明默认函数参数值。

常见编程错误6.9

在头文件和成员函数定义中指定同一成员函数的默认初始化值。

图6.8的程序初始化五个Time对象:一个将三个参数指定为默认值,一个指定一个参数,一个指定两个参数.一个指定三个参数,一个指定三个无效参数。每个对象数据成员均显示实例化和初始化之后的内容。

如果类不定义构造函数,则编译器生成默认构造函数。这种构造函数不进行任何初始化,因此生成对象时,不能保证处于一致状态。

软件工程视点6.20

类不一定有默认构造函数。