当一个对象作为一个类的成员时,那么这个类和这个对象成员的关系称为“has-a"(有一个)的关系,也称为”组成“ 。
在创建对象时,构造函数会被自动调用。这里,将讨论构造函数如何通过成员初始化器 来完成将参数传递给成员对象的构造函数的任务。
成员对象以在类的定义中声明的顺序,并在包含他们的对象(也有称为”宿主对象“)构造之前建立。注意,成员对象在类定义中声明顺序不是构造函数的成员初始化列表中的顺序。
测试代码( VS2010 )
stdafx.h :
[C++] 纯文本查看 复制代码
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <cstring> // strlen and strncpy prototypes
// TODO: 在此处引用程序需要的其他头文件
Date.h :
[C++] 纯文本查看 复制代码 #ifndef DATE_H
#define DATE_H
class Date
{
public:
Date( int = 1, int = 1, int = 1900 ); // default constructor
void print() const; // print date in month/day/year format
~Date(); // provided to confirm destruction order
private:
int month; // 1-12 (January-December)
int day; // 1-31 based on month
int year; // any year
// utility function to check if day is proper for month and year
int checkDay( int ) const;
}; // end class Date
#endif
Date.cpp :
[C++] 纯文本查看 复制代码 // Fig. 10.11: Date.cpp
// Member-function definitions for class Date.
#include "stdafx.h"
using std::cout;
using std::endl;
#include "Date.h" // include Date class definition
// constructor confirms proper value for month; calls
// utility function checkDay to confirm proper value for day
Date::Date( int mn, int dy, int yr )
{
if ( mn > 0 && mn <= 12 ) // validate the month
month = mn;
else
{
month = 1; // invalid month set to 1
cout << "Invalid month (" << mn << ") set to 1.\n";
} // end else
year = yr; // could validate yr
day = checkDay( dy ); // validate the day
// output Date object to show when its constructor is called
cout << "Date object constructor for date ";
print();
cout << endl;
} // end Date constructor
// print Date object in form month/day/year
void Date::print() const
{
cout << month << '/' << day << '/' << year;
} // end function print
// output Date object to show when its destructor is called
Date::~Date()
{
cout << "Date object destructor for date ";
print();
cout << endl;
} // end ~Date destructor
// utility function to confirm proper day value based on
// month and year; handles leap years, too
int Date::checkDay( int testDay ) const
{
static const int daysPerMonth[ 13 ] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// determine whether testDay is valid for specified month
if ( testDay > 0 && testDay <= daysPerMonth[ month ] )
return testDay;
// February 29 check for leap year
if ( month == 2 && testDay == 29 && ( year % 400 == 0 ||
( year % 4 == 0 && year % 100 != 0 ) ) )
return testDay;
cout << "Invalid day (" << testDay << ") set to 1.\n";
return 1; // leave object in consistent state if bad value
} // end function checkDay
Employee.h :
[C++] 纯文本查看 复制代码 // Fig. 10.12: Employee.h
// Employee class definition.
// Member functions defined in Employee.cpp.
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
#include "Date.h" // include Date class definition
class Employee
{
public:
Employee( const char * const, const char * const,
const Date &, const Date & );
void print() const;
~Employee(); // provided to confirm destruction order
private:
char firstName[ 25 ];
char lastName[ 25 ];
const Date birthDate; // composition: member object
const Date hireDate; // composition: member object
}; // end class Employee
#endif
Employee.cpp :
[C++] 纯文本查看 复制代码 // Fig. 10.13: Employee.cpp
// Member-function definitions for class Employee.
#include <stdafx.h>
using std::cout;
using std::endl;
using std::strlen;
using std::strncpy;
#include "Employee.h" // Employee class definition
#include "Date.h" // Date class definition
// constructor uses member initializer list to pass initializer
// values to constructors of member objects birthDate and hireDate
// [Note: This invokes the so-called "default copy constructor" which the
// C++ compiler provides implicitly.]
Employee::Employee( const char * const first, const char * const last,
const Date &dateOfBirth, const Date &dateOfHire )
: birthDate( dateOfBirth ), // initialize birthDate
hireDate( dateOfHire ) // initialize hireDate
{
// copy first into firstName and be sure that it fits
int length = strlen( first );
length = ( length < 25 ? length : 24 );
strncpy( firstName, first, length );
firstName[ length ] = '\0';
// copy last into lastName and be sure that it fits
length = strlen( last );
length = ( length < 25 ? length : 24 );
strncpy( lastName, last, length );
lastName[ length ] = '\0';
// output Employee object to show when constructor is called
cout << "Employee object constructor: "
<< firstName << ' ' << lastName << endl;
} // end Employee constructor
// print Employee object
void Employee::print() const
{
cout << lastName << ", " << firstName << " Hired: ";
hireDate.print();
cout << " Birthday: ";
birthDate.print();
cout << endl;
} // end function print
// output Employee object to show when its destructor is called
Employee::~Employee()
{
cout << "Employee object destructor: "
<< lastName << ", " << firstName << endl;
} // end ~Employee destructor
main.cpp :
[C++] 纯文本查看 复制代码 // main.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
using std::cout;
using std::endl;
#include "Employee.h" // Employee class definition
int _tmain(int argc, _TCHAR* argv[])
{
Date birth( 7, 24, 1949 );
Date hire( 3, 12, 1988 );
Employee manager( "Bob", "Blue", birth, hire );
cout << endl;
manager.print();
cout << "\nTest Date constructor with invalid values:\n";
Date lastDayOff( 14, 35, 1994 ); // invalid month and day
cout << endl;
return 0;
return 0;
}
运行输出:Date object constructor for date 7/24/1949
Date object constructor for date 3/12/1988
Employee object constructor: Bob Blue
Blue, Bob Hired: 3/12/1988 Birthday: 7/24/1949
Test Date constructor with invalid values:
Invalid month (14) set to 1.
Invalid day (35) set to 1.
Date object constructor for date 1/1/1994
Date object destructor for date 1/1/1994
Employee object destructor: Blue, Bob
Date object destructor for date 3/12/1988
Date object destructor for date 7/24/1949
Date object destructor for date 3/12/1988
Date object destructor for date 7/24/1949
Employee 类的构造函数的参数 dateOfBirth 被传递给 birthDate 对象的构造函数,参数 dateOfHire 被传递给 hireDate 对象的构造函数。
在 Date 类中,并没有提供接收一个 Date 类型参数的构造函数,那么 Employee 类构造函数中的成员初始化列表是怎样通过将 Date 对象的参数传递给 Date 构造函数来初始化 birthDate 和 hireDate 的呢?原因是,编译器提供给每个类一个默认的拷贝构造函数,该函数将构造函数的参数对象的每个成员复制给将要初始化的对象的相应成员。
如果成员对象不是用成员初始化列表进行初始化,并且成员对象的类没有提供默认的构造函数(换言之,成员对象的类定义了一个或多个构造函数,但没有一个默认的构造函数),将产生一个编译错误。
观察输出结果,可以了解到:对象由内而外的创建,析构由外而内的撤销。下面简要分析这个情况:
1. 第 1 个析构输出“Date object destructor for date 1/1/1994” 。lastDayOff 这个对象是最后创建的,也就是在最外一层,因此首先被析构。
2. 第 2-4 个析构输出“Employee object destructor: Blue, Bob” ,“Date object destructor for date 3/12/1988”, “Date object destructor for date 7/24/1949” 。在 Employee manager( "Bob", "Blue", birth, hire ); 语句中,默认复制构造函数首先会初始化两个对象 birthDate 和 hireDate ,然后才是 manager 。因此在析构时,首先是 Employee 类对象 manager 的析构,接着到 hireDate,再到 birthDate --- 这是从 birthDate 和 hireDate 这两个对象的声明顺序决定的,因此析构时是从反顺序进行。
3. 第 5-6 个析构就是 main() 函数中一开始初始化的两个对象 birth 和 hire 。析构顺序从程序的执行顺序是先 hire 到 birth 。 |