Teach Yourself Visual C++ 6 in 21 Days
- 16 -
Creating Your Own Classes and Modules
Sometimes you need to build a set of application functionality that will be used
in an application that another programmer is working on. Maybe the functionality
will be used in a number of applications. Another possibility is that you want to
separate some functionality from the rest of the application for organizational purposes.
You might develop this separate set of functionality and then give a copy of the
code to your friend to include in his application, but then every time you make any
changes to your set of functionality, it has to be reincorporated into the other
set of application code. It would be much more practical if you could give a compiled
version of your functionality to the other programmer so that every time you updated
your part, all you had to hand over was a new compiled file. The new file could just
replace the previous version, without having to make any changes to the other programmer's
code.
Well, it is possible to place your set of functionality into a self-contained
compiled file, link it into another programmer's application, and avoid adding any
new files to the finished application. Today, you will learn
- How to design your own classes.
- How to create compiled modules that can be linked into other applications.
- How to include these modules into an application.
Designing Classes
You've already designed and built your own classes over the past few days, so
the basics of creating a new class is not a new topic. Why did you create these classes?
Each of the new classes that you created encapsulated a set of functionality that
acted as a self-contained unit. These units consisted of both data and functionality
that worked together to define the object.
Encapsulation
Object-oriented software design is the practice of designing software in the same
way that everything else in the world is designed. For instance, you can consider
your car built from a collection of objects: the engine, the body, the suspension,
and so on. Each of these objects consists of many other objects. For instance, the
engine contains either the carburetor or the fuel injectors, the combustion chamber
and pistons, the starter, the alternator, the drive chain, and so on. Once again,
each of these objects consists of even more objects.
Each of these objects has a function that it performs. Each of these objects knows
how to perform its own functions with little, if any, knowledge of how the other
objects perform their functions. Each of the objects knows how it interacts with
the other objects and how they are connected to the other objects, but that's about
all they know about the other objects. How each of these objects work internally
is hidden from the other objects. The brakes on your car don't know anything about
how the transmission works, but if you've got an automatic transmission, the brakes
do know how to tell the transmission that they are being applied, and the transmission
decides how to react to this information.
You need to approach designing new classes for your applications in the same way.
The rest of the application objects do not need to know how your objects work; they
only need to know how to interact with your objects. This principle, called encapsulation,
is one of the basic principles of object-oriented software.
Inheritance
Another key principle of object-oriented software design is the concept of inheritance.
An object can be inherited from another object. The descendent object inherits all
the existing functionality of the base object. This allows you to define the descendent
object in terms of how it's different from the base object.
Let's look at how this could work with a thermostat. Suppose you had a basic thermostat
that you could use in just about any setting. You could set the temperature for it
to maintain, and it would turn on the heating or the air-conditioning as needed to
maintain that temperature. Now let's say you needed to create a thermostat for use
in a freezer. You could start from scratch and build a customized thermostat, or
you could take your existing thermostat and specify how the freezer version differs
from the original. These differences might include that it's limited to turning on
the air conditioning and could never turn on the heater. You would probably also
put a strict limit on the range of temperatures to which the thermostat could be
set, such as around and below 32[infinity] Fahrenheit, or 0[infinity] Celsius. Likewise,
if you needed a thermostat for an office building, you would probably want to limit
the temperature range to what is normally comfortable for people and not allow the
temperature to be set to an extremely cold or hot setting.
With inheritance in creating your own classes, this method just described represents
the same principle that you want to apply. If possible, you should start with an
existing C++ class that has the basic functionality that you need and then program
how your class is different from the base class that you inherited from. You have
the ability to add new data elements, extend existing functionality, or override
existing functionality, as you see fit.
Visual C++ Class Types
In most application projects, when you are creating a new class, you have a few
options on the type of class that you are creating. These options are
- Generic class
- MFC class
- Form class
Which of these types of classes you choose to create depends on your needs and
what your class will be doing. It also depends on whether your class needs to descend
from any of the MFC classes.
Generic Class
You use a generic class for creating a class that is inherited from a class you
have already created. This class type is intended for creating classes that are not
inherited from any MFC classes (although you have already seen where you need to
use it to create classes that are based on MFC classes). If you want to create a
more specialized version of the CLine class, for instance, a CRedLine class, that
only drew in red, you create it as a generic class because it's inherited from another
class that you created.
When you create a generic class, the New Class Wizard tries to locate the declaration
of the base class (the header file with the class declared). If it cannot find the
appropriate header file, it tells you that you might need to make sure that the header
file with the base class definition is included in the project. If the base class
happens to be an MFC class that is not accessible as an MFC class (such as CObject),
then you can ignore this warning because the correct header file is already part
of the project.
MFC Class
If you want to make a reusable class that is based on an existing MFC class, such
as an edit box that automatically formats numbers as currency, you want to create
an MFC class. The MFC class type is for creating new classes that are inherited from
existing MFC classes.
Form Class
The form class is a specialized type of MFC class. You need to create this type
of class if you are creating a new form style window. It can be a dialog, form view,
or database view class. This new class will be associated with a document class for
use with the view class. If you are building a database application, you will probably
create a number of this style of classes.
Creating Library Modules
When you create new classes for your application, they might be usable in other
applications as well. Often, with a little thought and effort, classes you create
can be made flexible enough so that they could be used in other applications. When
this is the case, you need some way of packaging the classes for other applications
without having to hand over all your source code. This is the issue that library
modules address. They allow you to compile your classes and modules into a compiled
object code library that can be linked into any other Visual C++ application.
Library modules were one of the first means available to provide compiled code
to other programmers for use in their applications. The code is combined with the
rest of the application code by the linker as the final step in the compilation process.
Library modules are still a viable means of sharing modules with other developers.
All the developer needs is the library (.lib) file and the appropriate header files
that show all the exposed classes, methods, functions, and variables, which the other
programmer can access and use. The easiest way to do this is to provide the same
header file that you used to create the library file, but you can also edit the header
so that only the parts that other programmers need are included.
By using library files to share your modules with other programmers, you are arranging
that your part of the application is included in the same executable file as the
rest of the application. Your modules are not included in a separate file, such as
a DLL or ActiveX control. This results in one less file to be distributed with the
application. It also means that if you make any changes to the module, fix any bugs,
or enhance any functionality, then the applications that use your module must be
relinked. Using library files has a slight disadvantage to creating DLLs, where you
may be able to just distribute the new DLL without having to make any changes to
the application, but you'll learn all about that tomorrow.
Using Library Modules
To get a good idea of how to use library modules, it's helpful to create a library
module, use it in another application, and then make some modifications to the library
module. For today's sample application, you'll create a module that generates a random
drawing on the window space specified. It'll be able to save and restore any of these
drawings. You'll then use this module in an SDI application, where every time a new
document is specified, a new drawing is generated. The initial module will only use
eight colors and will generate only a limited number of line sequences. Later, you'll
modify the module so that it will generate any number of colors and will generate
a larger number of line sequences.
Creating the Library Module
To create a library module project, you need to specify in the New dialog that
you want to create a Win32 Static Library, as shown in Figure 16.1. This tells Visual
C++ that the output from the project compilation will be a library module instead
of an executable application. From there, all you have to do is define the classes
and add the code. You have the options of including support for MFC and using precompiled
headers in your project, as in Figure 16.2, the only step in the Project Wizard.
The library that you will create for today's sample application will consist of
two classes. The first class will be the CLine class that you first created on Day
10, "Creating Single Document Interface Applications." The second class
will be the class that creates the random drawings on the drawing surface. This class
will contain an object array of the CLine objects that it will create and populate
with each of the drawing efforts. This second class will also need functionality
to save and restore the drawing, as well as to delete the existing drawing so that
a new drawing can be started. It will need to know the dimensions of the drawing
area so that it can generate a drawing that will fit in the drawing area. Once you
create this module, you'll take a look at how you can use this module in an application
project.
FIGURE 16.1. Specifying
a library module project.
FIGURE 16.2. Specifying
project support options.
Creating a Library Project
To start the library project for today's example, you need to create a new project,
specifying that the project is a Win32 Static Library project. Give the project a
suitable name and click OK to create the project.
For today's sample project, specify on the one wizard step to include both MFC
and precompiled header support. Although the precompiled header support is not necessary,
it will speed up most compiles that you perform while building the module.
Once you create your module project, you'll find yourself working with a project
that has no classes. You've got a blank slate from which you can create
|