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

Chapter 22: Factoring Classes 291

See how pure virtual functions correct the problem. Here’s the same situation with Account declared as an abstract class:

class Account

{

public:

//just like preceding

//declare withdrawal pure virtual virtual void withdrawal(float amnt) = 0;

};

class Savings : public Account

{

public:

virtual void withdrawal(float amnt);

};

void fn(Account *pAcc)

{

// withdraw some money pAcc->withdrawal(100.00F); // now it works

};

int main( )

{

Savings s; // open an account fn(&s);

// ...same as before...

}

The situation is the same except the class Account includes the member function withdrawal(). Now when the compiler checks to see whether pAcc->withdrawal() is defined, it sees the definition of Account:: withdrawal() just as it expects. The compiler is happy. You’re happy. That makes me happy, too. (Frankly, a football game and a cold beer are enough to make me happy.)

The pure virtual function is a placeholder in the base class for the subclass to override with its own implementation. Without that placeholder in the base class, there is no overriding.

Factoring C++ Source Code

Factoring a problem has a physical side. Classes that have been factored out of the jumble of separate concepts that make up a program should be moved into their own “space.”

292 Part IV: Inheritance

The programmer can divide a single program into separate files known as modules. These individual source files are compiled separately and then com­ bined during the build process to generate a single program. Modules can then be allocated to separate groups known as namespaces.

The process of combining separately compiled modules into a single exe­ cutable is called linking.

There are a number of reasons to divide programs into more manageable pieces. First, dividing a program into modules results in a higher level of encapsulation. Classes wall off their internal members in order to provide a certain degree of safety. Programs can wall off functions to do the same thing.

Encapsulation is one of the advantages of object-oriented programming.

Second, it is easier to comprehend and, therefore, easier to write and debug a program that consists of a number of well-thought-out modules than a single source file full of all of the classes and functions that the program uses.

Next comes reuse. I used the reuse argument to help sell object-based pro­ gramming. It is extremely difficult to keep track of a single class reused among multiple programs when a separate copy of the class is kept in each program. It is much better if a single class module is automatically shared among programs.

Finally, there is the argument of time. A compiler such as Visual C++ or Dev- C++ doesn’t need very long to build the examples contained in this book using a high-speed computer like yours. Commercial programs sometimes consist of millions of source lines of code. Rebuilding a program of that size can take more than 24 hours. A programmer would not tolerate rebuilding a program like that for every single change. However, the majority of the time is spent compiling source files that haven’t changed. It is much faster to recompile just those modules that have changed and then quickly link all modules together.

Separate namespaces allow a further level of encapsulation. A namespace should consist of a set of modules that perform a single capability. For example, all of the mathematical functions might be combined into a Math namespace.

This lesson builds a simplistic program, called SeparateModules, that con­ sists of a Student class, a GraduateStudent subclass, and a main() module to test both.

Dividing the program — Student

You begin by deciding what the logical divisions of SeparateModules should be. First, you notice that Student is an entity of its own. It does not depend

Chapter 22: Factoring Classes 293

on any other functions (besides C++ functions). Thus, it would make sense to put Student in a module by itself. Because the class will be used in several places, you break the declaration into a student.h file and a separate imple­ mentation file, Student.cpp. By convention, the include file carries the name of the primary class it defines, but in lowercase letters. Ideally, the include file defines only one class. This allows the user program to include just the files that it needs.

Historically, all include files carried the extension .h. This was changed in the current C++ standard. System include files such as iostream now have no extension at all. However, many programmers stick with the .h convention for include files they write. This allows such include files to be easily differen­ tiated by the reader of the program.

The resulting student.h file appears as follows:

// Student - basic student #ifndef _STUDENT_

#define _STUDENT_

namespace Schools

{

class Student

{

public:

Student(char* pszName, int nID); virtual char* display();

protected:

// student’s name char* pszName; int nID;

};

}

#endif

The #ifndef is a preprocessor control much like #include. #ifndef _ STUDENT_ says to include only the following lines if the argument _STUDENT_ is defined. The first time that student.h is included, _STUDENT_ is not defined. However, the #define immediately following the #ifndef then defines it. This has the effect that student.h is processed only once, no matter how many times it is included in a given file.

Defining a namespace

The second feature of the Student class is the creation of the Schools namespace.

294 Part IV: Inheritance

A namespace is a collection of loosely coupled classes that are somehow logi­ cally similar. In this case, I intend to throw all classes that I create concerning students, graduate students, classes, course schedules, and so forth into the Schools namespace.

The classes that make up the Schools namespace are like members of a family. One class within a namespace may refer to other members of the same namespace directly. However, external classes must specify the namespace. You will see the ways of specifying a class’s namespace in the follow­ ing SeparatedMain application.

Another reason for dividing modules into namespaces is to avoid “name colli­ sion.” For example, the class Grade within the namespace Schools does not interfere with the class Grade in the namespace FoodProduction.

Implementing Student

I put the implementation of the Student class in the file Student.cpp:

// Student - implement the methods of the Student class #include <cstdio>

#include <cstdlib> #include <iostream> #include <string> #include “student.h”

namespace Schools

{

Student::Student(char* pszNameArg, int nIDArg) : nID(nIDArg)

{

pszName = new char[strlen(pszNameArg) + 1];

strcpy(pszName, pszNameArg);

}

// display - return a description of student char* Student::display()

{

//copy the student’s name into a block of heap

//memory that we can return to the caller char* pReturn = new char[strlen(pszName) + 1]; strcpy(pReturn, pszName);

return pReturn;

}

}

The constructor for Student copies off the name and id provided it. The vir­ tual display() method returns a string that describes the Student object.

Chapter 22: Factoring Classes 295

Compiling the Student.cpp file generates an intermediate file. This interme­ diate file can be combined quickly with other intermediate files to form a completed executable program.

For historical reasons, this intermediate file carries the extension .o (for “object file”) in most C++ environments.

Dividing the program — GraduateStudent

The next module that seems quasi-independent is GraduateStudent. Logically, one could fold the GraduateStudent class into Student.cpp; however, some programs may want to deal with Student as an abstraction and not worry about students versus graduate students.

I made the GraduateStudent class as simple as possible. The include file appears as follows:

// GraduateStudent - a special type of Student #ifndef _GRADUATE_STUDENT_

#define _GRADUATE_STUDENT_

#include “student.h” namespace Schools

{

class GraduateStudent : public Student

{

public:

//trivial constructors GraduateStudent(char* pszName, int nID)

:Student(pszName, nID){}

//demonstration virtual function virtual char* display();

};

}

#endif

Notice that the graduateStudent.h file includes student.h. This is because the GraduateStudent class is dependent upon the definition of Student.

The resulting source file implements the display() method, the only member function that is yet to be implemented:

// GraduateStudent - a special type of Student #include <cstdio>

#include <cstdlib> #include <iostream>

#include “graduateStudent.h”