Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ For Dummies (2004) [eng].pdf
Скачиваний:
84
Добавлен:
16.08.2013
Размер:
8.09 Mб
Скачать

210 Part III: Introduction to Classes

Different types of objects are created at different times. Global objects are cre­ ated when the program first begins execution. Local objects are created when the program encounters their declaration.

A global object is one that is declared outside of a function. A local object is one that is declared within a function and is, therefore, local to the function. In the following example, the variable me is global, and the variable notMe is local to the function pickOne():

int me = 0; void pickOne()

{

int notMe;

}

According to the rules, global objects are initialized to all zeros when the program starts executing. Objects declared local to a function have no particular initial value. Having all data members have a random state may not be a valid condition for all classes.

C++ allows the class to define a special member function that is invoked auto­ matically when an object of that class is created. This member function, called the constructor, must initialize the object to a valid initial state. In addi­ tion, the class can define a destructor to handle the destruction of the object. These two functions are the topics of this chapter.

Using Constructors

The constructor is a member function that is called automatically when an object is created. Its primary job is to initialize the object to a legal initial value for the class. (It’s the job of the remaining member functions to ensure that the state of the object stays legal.)

Why you need constructors

You could initialize an object as part of the declaration — that’s the way the

C programmer would do it — for example:

struct Student

{

int semesterHours; float gpa;

};

Chapter 16: “Why Do You Build Me Up, Just to Tear Me Down, Baby?” 211

void fn()

{

Student s1 = {0, 0.0};

// or Student s2;

s2.semesterHours = 0; s2.gpa = 0.0;

// ...function continues...

}

You could outfit the class with an initialization function that the application calls as soon as the object is created. Because this initialization function is a member of the class, it would have access to the protected members. This solution appears as follows:

class Student

{

public:

void init()

{

semesterHours = 0; gpa = 0.0;

}

// ...other public members...

protected:

int semesterHours; float gpa;

};

 

void fn()

 

{

 

Student s;

// create the object...

s.init();

// ...then initialize it

// ...function continues...

}

The only problem with this solution is that it abrogates the responsibility of the class to look after its own data members. In other words, the class must rely on the application to call the init() function. If it does not, the object is full of garbage, and who knows what might happen.

What is needed is a way to take the responsibility for calling the init() func­ tion away from the application code and give it to the compiler. Every time an object is created, the compiler can insert a call to the special init() function to initialize it. That’s a constructor!

212 Part III: Introduction to Classes

Making constructors work

The constructor is a special member function that’s called automatically when an object is created. It carries the same name as the class to differentiate it from the other members of the class. The designers of C++ could have made up a different rule, such as: “The constructor must be called init().” It wouldn’t have made any difference, as long as the compiler could recognize the constructor. In addition, the constructor has no return type, not even void, because it is only called automatically — if the constructor did return something, there would be no place to put it. A constructor cannot be invoked manually.

Constructing a single object

With a constructor, the class Student appears as follows:

//

// Constructor - example that invokes a constructor

//

#include <cstdio> #include <cstdlib> #include <iostream> using namespace std;

class Student

{

public:

Student()

{

cout << “constructing student” << endl; semesterHours = 0;

gpa = 0.0;

}

protected:

int semesterHours; float gpa;

};

int main(int nNumberofArgs, char* pszArgs[])

{

cout << “Creating a new Student object” << endl; Student s;

cout << “Creating a new object off the heap” << endl; Student* pS = new Student;

//wait until user is ready before terminating program

//to allow the user to see the program results system(“PAUSE”);

return 0;

}

Chapter 16: “Why Do You Build Me Up, Just to Tear Me Down, Baby?” 213

At the point of the declaration of s, the compiler inserts a call to the con­ structor Student::Student(). Allocating a new Student object from the heap has the same effect as demonstrated by the output from the program:

Creating a new Student object constructing student

Creating a new object off the heap constructing student

Press any key to continue . . .

This simple constructor was written as an inline member function. Constructors can be written also as outline functions, as shown here:

class Student

{

public:

Student();

// ...other public members...

protected:

int semesterHours; float gpa;

};

Student::Student()

{

cout << “constructing student” << endl; semesterHours = 0;

gpa = 0.0;

}

The output from this program can “prove” to you that constructors work as advertised, but to get the real effect, you really should single-step this simple program in your debugger. (See Chapter 10 for instructions on using the debugger.)

Single-step through this example until control comes to rest at the Student’s declaration. Select Step Into and control magically jumps to Student:: Student(). Continue single-stepping through the constructor. When the function has finished, control returns to the statement after the declaration.

In some cases, Step Into will actually execute the entire constructor without stopping. You may have to set a breakpoint in the constructor to get the effect. Setting a breakpoint always works.

Constructing multiple objects

Each element of an array must be constructed on its own. Making the follow­ ing simple change to the Constructor program contained in ConstructArray:

214 Part III: Introduction to Classes

//

//ConstructArray - example that invokes a constructor

//

on an array of objects

//

 

#include <cstdio>

 

#include <cstdlib>

 

#include <iostream>

 

using namespace std;

 

class Student

 

{

 

public:

 

Student()

 

{

cout << “constructing student” << endl; semesterHours = 0;

gpa = 0.0;

}

// ...other public members...

protected:

int semesterHours; float gpa;

};

int main(int nNumberofArgs, char* pszArgs[])

{

cout << “Creating an array of 5 Student objects” << endl; Student s[5];

//wait until user is ready before terminating program

//to allow the user to see the program results system(“PAUSE”);

return 0;

}

generates the following output:

Creating an array of 5 Student objects constructing student

constructing student constructing student constructing student constructing student

Press any key to continue . . .

Constructing a duplex

If a class contains a data member that is an object of another class, the con­ structor for that class is called automatically as well. Consider the following ConstructMembers example program. I added output statements so that you can see the order in which the objects are invoked.

Chapter 16: “Why Do You Build Me Up, Just to Tear Me Down, Baby?” 215

//

//ConstructMembers - the member objects of a class

//

are each constructed before the

//

container class constructor gets

//

a shot at it

//

 

#include <cstdio>

 

#include <cstdlib>

 

#include <iostream>

 

using namespace std;

 

class Course

 

{

 

public:

 

Course()

 

{

 

cout << “constructing course” << endl;

}

};

class Student

{

public:

Student()

{

cout << “constructing student” << endl; semesterHours = 0;

gpa = 0.0;

}

protected:

int semesterHours; float gpa;

};

class Teacher

{

public:

Teacher()

{

cout << “constructing teacher” << endl;

}

protected: Course c;

};

class TutorPair

{

public:

TutorPair()

{

cout << “constructing tutorpair” << endl; noMeetings = 0;

}

216 Part III: Introduction to Classes

protected: Student student; Teacher teacher;

int noMeetings;

};

int main(int nNumberofArgs, char* pszArgs[])

{

cout << “Creating TutorPair object” << endl; TutorPair tp;

//wait until user is ready before terminating program

//to allow the user to see the program results system(“PAUSE”);

return 0;

}

Executing this program generates the following output:

Creating TutorPair object constructing student constructing course

constructing teacher constructing tutorpair

Press any key to continue . . .

Creating the object tp in main automatically invokes the constructor for TutorPair. Before control passes into the body of the TutorPair construc­ tor, however, the constructors for the two-member objects, student and teacher, are invoked.

The constructor for Student is called first because it is declared first. Then the constructor for Teacher is called.

The member Teacher.c of class Course is constructed as part of building the Teacher object. The Course constructor gets a shot first. Each object within a class must construct itself before the class constructor can be invoked. Otherwise, the main constructor would not know the state of its data members.

After all member data objects have been constructed, control returns to the open brace, and the constructor for TutorPair is allowed to construct the remainder of the object.

It would not do for TutorPair to be responsible for initializing Student and Teacher. Each class is responsible for initializing its own objects.