Microsoft Visual BASIC in C++ 2022
Microsoft Visual BASIC in C++ 2022
All rights reserved. No part ofthis publication may be reproduced, stored in a retrieval system, or
transmitted, in any form or by any means, electronic, mechanical, photocopying, recording or
otherwise, without the prior permission ofthe copyright owners.
DISCLAIMER
Errors, if any, are purely unintentional and readers are requested to communicate such errors to
the publisher to avoid discrepancies in future.
Published by:
AN ISO 9001:2008 CERTIFIED COMPANY
Chapter 6 you stall using the GUI based development environment to make the programs.
It teaches how the VC++ program works.
Chapter 7 covers the windows, dialog boxes and controls.
Chapter 8 covers Dialogs and Property Sheets in Visual C++.
Contents
2. CLASS IN C++.................................................................................... 19
2.1 FUNDAMENTALS.......................................................................................... 19
2.1.1 Class................................................................................................................ 19
2.1.2 Object.............................................................................................................. 20
2.1.3 Instance........................................................................................................... 20
2.1.4 Method............................................................................................................. 20
2.1.5 Message Passing ......................................................................................... 20
2.1.6 Inheritance ...................................................................................................... 21
2.1.7 Multiple Inheritance ....................................................................................... 21
2.1.8 Abstraction ..................................................................................................... 21
2.1.9 Encapsulation ................................................................................................ 22
2.1.10 Polymorphism ............................................................................................... 22
2.1.11 Decoupling ..................................................................................................... 23
2.1.12 Dynamic Binding of Function Calls .......................................................... 23
2.2 CLASS DEFINITION...................................................................................... 23
2.3 CONSTRUCTORS AND DESTRUCTORS ............................................... 27
2.3.1 Overloading Constructors .......................................................................... 30
2.3.2 Default Constructor ...................................................................................... 31
2.3.3 Copy Constructors ....................................................................................... 32
2.4 POINTERS TO CLASSES ...................................................................... 35
2.5 CLASSES DEFINED WITHSTRUCT AND UNION.................................. 37
2.6 THE KEYWORD THIS ................................................................................... 37
2.7 STATIC MEMBERS....................................................................................... 38
2.8 INLINE FUNCTIONS...................................................................................... 39
2.9 ACCESS SPECIFIERS ............................................................................... 42
Visual C++ 9
OR Visit
freebie.kartbucket.com
CHAPTER
INTRODUCTION TO
VC++
Visual C++ is more complicated than programming in C++ on a text based system for
three main reasons:
programming wizards (AppWizard and ClassWizard), which help you to generate the
basic source code for your programs, define C++ classes, handle Windows messages,
and perform other tasks. You can build and execute your programs from within the
Developer Studio, which automatically runs the optimizing compiler, Ihc incremental
linker, and any other required build tools. You can also debug programs using the
integrated debugger, and you can view and manage program symbols and C++ classes
using the ClassView window.
1.2.5 ActiveX
This component installs ActiveX controls that you can add to the Windows programs
you create using the Microsoft Foundation Classes library. ActiveX controls are reusable
software components that can perform a wide variety of tasks.
Zero to Mastery Microsoft Visual in C++
1.2.8 Graphics
This component consists of graphics elements (metafiles, bitmaps, cursors and icons)
as well as video clips that you can add to your programs.
1.2.9 Tools
The tools component of Visual C++ comprises the following supplemental development
tools:
• API Text Viewer
• MS Info
• MFC Trace Utility
• Spy++
• win 32 SDK Tools
• OLE/Com Object Viewer
• ActiveX Control Test Container
• VC Error Lookup
Introduction to VC++
Before you begin your quick lour around the Visual C++ development environment,
you should stall Visual C++ on your computer so that you can see firsthand how each
of the areas are arranged and how you can change and alter that arrangement yourself.
After Developer Studio (the Microsoft Visual development environment) starts, you
see a window that looks like Figure I J. Each of the areas has a specific purpose in the
Developer Studio environment. You can rearrange these areas to customize the
Developer Studio environment so that it suits your particular development needs.
м Odiptii
Workspace
V Blandard
Build
* SuikdMirn0.br
ML
Aezouics
Edit
Debug
Eiwte
WhraicBar
Cu:tamiba..
Another way that you can easily rearrange your development environment is to grab
the double bars at the left end of any of the toolbars or panes with the mouse. You can
drag the toolbars away from where they are currently docked, making them floating
toolbars, as in Figure L3. You can drag these toolbars (and panes) to any other edge
of the Developer Studio to dock them in a new spot. Even when the toolbars are
docked, you can use the double bars to drag the toolbar left and right to place the
toolbar where you want it to be located.
Note: On the workspace and Output panes, the double bars that you can use (o drag the pane
around the Developer Studio environment might appear on the top of the pane or on the left
side, depending on how and where the pane is docked.
Figure 1.5: If the user clicks the first button, a simple greeting is shown
N«4
1J5 N F L A th Lurlrn\J?-ird
я1КГЕ rtppWirirc |dl) Lien c ritv* rtj fciJM
JJhl-!' Лрр\?гтдг |pjd|
fl L IM’/Rqccl
iJ|v*hV ЛЧНкЬп
М/п32 Ji;oh \zolcJtian
j| CjimJ ]
4. In Step 3 of the AppWizard, leave the defaults for including source file comments
and using the MFC library as a DLL. Click Next at the bottom of the wizard to
proceed to the final AppWizard step.
5. The final step of the AppWizard shows you the C++ classes that the AppWizard
will create for your application. Click Finish to let AppWizard generate your
application shell.
6. Before AppWizard creates your application shell, it presents you with a list of
what it is going to put into the application shell, as shown in Figure 1.7, based
on the options you selected when going through the AppWizard. Click OK and
AppWizard generates your application.
7. After ihe AppWizard generates your application shell, you are returned to the
Developer Studio environment. You will notice that the workspace pane now
presents you with a tree view of the classes in your application shell, as in
Figure 1,8. You might also be presented with the main dialog window in the
editor area of the Developer Studio area.
Introduction to VC++
Figure 1.8: Your workspace with a tree view of the project’s classes
5. Right-click the mouse over I he Cancel button, opening the pop-up menu in
Figure 1.13. Select Properties from the menu, and the properties dialog in Figure
1.14 opens.
6* Change the value in the Caption field to &Close* Close the properties dialog by
clicking the Close icon in the upper-right corner of the dialog*
7. Move and resize the OK button to around the middle of the window, as in
Figure 1.15.
8. On the OK button properties dialog, change the ID value to 1DHELL0 and the
caption to &Hello*
9. Now when you compile and run your application, it will look like what you’ve
just designed, as shown in Figure 1.16.
Note: If you play with your application, you will notice that the Close button still closes the
application. However, the Hello button no longer does anything because you changed the ID of
the button. MFC applications contain a series of macros in the source code that determine which
functions to call based on the ID and event message of each control in the application. Because
you changed the ID of the Hello button, these macros no longer know which function to call
when the button is clicked.
MHlbW JUTiIrtbt
/ ? jDj cf^lwnut
W JnlnlDJog □N_WU_.KirDALCG
W M₽3hi D'JWM PW
Ч1 ■J4>/I.i_ J JH-'r-LH/h LL H
V J-iS-чСин iol d D4 VM 3?j23MHAJP
Qi- ] Caned
3. With 1DHELL0 selected in the Object ID list, select BN_CLICKED in the list
of messages and click Add Function. This opens the Add Member Function
dialog shown in Figure 1.18. This dialog contains a suggestion for the function
name, Click OK to create the function and add it to the message map.
4. After the function is added for the click message on the Hello button, select the
OnHello function in the list of available functions, as in Figure 1.19. Click the
Edit Code button so that your cursor is positioned in the source code for the
function, right at the position where you should add your functionality.
5* Add the code in Listing 1.1 just below the TODO comment line, as shown in
Figure L2CL
Figure 1.20: Source code view where you insert Listing 1.1
1: Void CHelloDlg::OnHello()
2:{
3: U TODO: Add your control notification handler code here
4:
5: ///////////////////////
6: //MY CODE STARTS HERE
7: ///////////////////////
8:
9: // Say hello to the user
10: MessageBox(“Hello. This is my first Visual C++ Application!”);
Zero to Mastery Microsoft Visual in C++
11:
12: lllllllllllllllllllllll
13: // MY CODE ENDS HERE
14: ///////////////////////
15: }
6. When you compile and run your application, the Hello button should display the
message shown in Figure 1.2L
REVIEW EXERCISE
CLASS IN C++
2.1 FUNDAMENTALS
2.1.1 Class
Defines the abstract characteristics of a thing (object), including the thing’s
characteristics (its attributes, fields or properties) and the thing’s behaviors (the things
it can do, or methods, operations or features). One might say (hat a class is a blueprint
or factory that describes the nature of something. For example, the class Dog would
consist of traits shared by all dogs, such as breed and fur color (characteristics), and
the ability to bark and sit (behaviors). Classes provide modularity and structure in an
object-oriented computer program. A class should typically be recognizable to a non
programmer familiar with ihe problem domain, meaning that the characteristics of
the class should make sense in context. Also, the code for a class should be relatively
self-contained (generally using encapsulation). Collectively, the properties and methods
defined by a class are called members.
Zero to Mastery Microsoft Visual in C++
2.1.2 Object
An object doesn't exist until an instance of the class has been created; the class is just
a definition* When the object is physically created, space for that object is allocated in
RAM. It is possible to have multiple objects created from one class* It can be considered
as a pattern (exemplar) of a class* The class of Dog defines all possible dogs by listing
the characteristics and behaviour they can have; the object Lassie is one particular
dog, with particular versions of the characteristics* A Dog has fur; Lassie has brown-
and-white fur*
2.1.3 Instance
One can have an instance of a class or a particular object. The instance is the actual
object created at runtime* In programmer jargon, the Lassie object is an instance of
the Dog class* The set of values of the attributes of a particular object is called its
state* The object consists of state and the behaviour that’s del med in the object's class*
2.1.4 Method
In language, methods (sometimes referred to as 'functions') arc verbs* Lassie, being
a Dog, has the ability to bark. So bark() is one of Lassie’s methods. She may have
other methods as well, for example sit() or eat() or walk() or save_timmy(). Within
the program, using a method usually affects only one particular object; all Dogs can
bark, but you need only one particular dog to do the barking*
sent between them. The syntax varies between languages, for example, In Java code
level message passing corresponds to “method calling”. Some dynamic languages use
double-dispatch or multi-dispatch to find and pass messages.
2.1.6 Inheritance
Inheritance is the mechanism whereby specific classes are made from more general
ones. The child or derived class inherits all the features of its parent or base class, and
is free to add features of its own. In addition, this derived class may be used as the
base class of an even more specialized class. Inheritance, or derivation, provides a
clean mechanism whereby common classes can share their common features, rather
than having to rewrite them. For example, consider a graph class which is represented
by edges and vertices and some (abstract) method of traversal. Next, consider a tree
class which is a special form of a graph. We can simply derive tree from graph and the
tree class automatically inherits the concept о I edges, vertices and traversal from the
graph class. We can then restrict how edges and vertices are connected within the tree
class so that it represents the true nature of a tree. Inheritance is supported in C++ by
placing the name of the base class after the name of the derived class when the derived
class is declared. It should be noted that a standard conversion occurs in C++ when a
pointer or reference to a base class is assigned a pointer or reference to a derived class.
2.1.8 Abstraction
Abstraction is simplifying complex reality by modeling classes appropriate to the
problem, and working at the most appropriate level of inheritance for a given aspect
of the problem. For example, Lassie the Dog may be treated as a Dog much of the
time, a Collie when necessary to access Co rhe-specific attributes or behaviors, and as
an Animal (perhaps the parent class of Dog) when counting Timmy’s pets.
Abstraction is also achieved through Composition. For example, a class Car would be
made up of an Engine, Gearbox, Steering objects, and many more components. To
build the Car class, one does not need to know how the different components work
internally, but only how to interface with them, z>., send messages to them, receive
messages from them, and perhaps make the different objects composing the class
interact with each other.
Zero to Mastery Microsoft Visual in C++
2.1.9 Encapsulation
Data encapsulation, sometimes referred to as data hiding, is the mechanism whereby
the implementation details of a class are kept hidden from the user. The user can only
perform a restricted set of operations on the hidden members of the class by executing
special functions commonly called methods. The actions performed by the methods
are determined by the designer of the class, who must be careful not to make the
methods either overly flexible or too restrictive. This idea of hiding the details away
from the user and providing a restricted, clearly defined interface is the underlying
theme behind the concept of an abstract data type.
The advantage of using data encapsulation comes when the implementation of the
class changes but the interface remains the same. For example, to create a stack class
which can contain integers, the designer may choose to implement it with an array,
which is hidden from the user of the class. The designer then writes the push() and
pop() methods which puts integers into the array and removes them from the array
respectively. These methods are made accessible to the user. Should an attempt be
made by the user to access the array directly, a compile lime error will result. Now,
should the designer decide to change the stack’s implementation to a linked list, the
array can simply be replaced with a linked list and the push() and popO methods
rewritten so that they manipulate the linked list instead of the array. The code which
the user has written to manipulate the stack is still valid because it was not given direct
access to the array to begin with.
The concept of data encapsulation is supported in C++ through the use ol the public,
protected and private keywords which arc placed in the declaration of the class. Anything
in the class placed after the public keyword is accessible to all the users of the class;
elements placed after the protected keyword are accessible only to the methods of the
class or classes derived from that class; elements placed after the private keyword are
accessible only to the methods of the class.
2.1.10 Polymorphism
Polymorphism allows the programmer to treat derived class members just like their
parent class’ members. More precisely, Polymorphism in object-oriented programming
is the ability of objects belonging to different data types to respond to method calls of
methods of the same name, each one according to an appropriate type-specific behavior.
One method, or an operator such as +, or *, can be abstractly applied in many
different situations. If a Dog is commanded to speak(), this may elicit a bark(). However,
if a Pig is commanded to speak(), this may elicit an oink(). They both inherit speakQ
from Animal, but their derived class methods override the methods of the parent
class; this is Overriding Polymorphism. Overloading Polymorphism is the use of one
method signature, or one operator such as 4*, to perform several different functions
depending on the implementation. The ‘+* operator, for example, may be used to
perform integer addition, float addition, list concatenation, or string concatenation.
Any two subclasses of Number, such as Integer and Double, are expected to add
together properly in an OOPS language. The language must therefore overload the
addition operator, *+’, to work this way. This helps improve code readability. How
this is implemented varies from language to language, but most OOPS languages
support at least some level of overloading polymorphism. Many OOPS languages also
support Parametric Polymorphism, where code is written without mention of any
specific type and thus can be used transparently with any number of new types. Pointers
are an example of a simple polymorphic routine that can be used with many different
types of objects.
2.1.11 Decoupling
Decoupling allows for the separation of object interactions from classes and inheritance
into distinct layers of abstraction. A common use of decoupling is to polymorphically
decouple the encapsulation, which is the practice of using reusable code to prevent
discrete code modules from interacting with each other. Not all of the above concepts
are to be found in all object-oriented programming languages, and so object-oriented
programming that uses classes is called sometimes class-based programming. In
particular, proto type-based programming does not typically use classes. As a result, a
significantly different yet analogous terminology is used to define the concepts of
object and instance.
A class is an expanded concept of a data structure: Instead of holding only data, it can
hold both data and functions. An object is an instantiation of a class. In terms of
variables, a class would be the type, and an object would be the variable. A class is a
definition of an object. It’s a type just like int. A class resembles a struct with just one
difference: all struct members are public by default. All class members are private.
Remember: A class is a type, and an object of this class is just a variable. Before we
can use an object, it must be created. The simplest definition of a class is
class name {
// members
2 Zero to Mastery Microsoft Visual in C++
Classes are generally declared using the keyword class, with the following format:
class class_name {
access_specifier_l:
memberl;
access_specifier_2:
member2;
} object_names;
Where class_name is a valid identifier for the class, object_names is an optional list of
names for objects of this class. The body of the declaration can contain members that
can be either data or function declarations, and optionally access specifiers.
All is very similar to the declaration on data structures, except that we can now
include also functions and members, but also this new thing called access specifier.
An access specifier is one of the following three keywords: private, public or protected.
These specifiers modify the access rights that the members following them acquire:
• Private members of a class arc accessible only from within other members of the
same class or from their friends.
• Protected members are accessible from members of their same class and from
their friends, but also from members of their derived classes.
• Finally, public members are accessible from anywhere where the object is visible.
By default, all members of a class declared with the class keyword have private access
for all its members. Therefore, any member that is declared before one other class
specifier automatically has private access. For example:
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void);
} rect;
Declares a class (i.e., a type) called CRectangle and an object (i.e., a variable) of this
class called rect. This class contains four members: two data members of type int
(member x and member y) with private access (because private is the default access
level) and two member functions with public access: set_values() and area(), of which
for now we have only included their declaration, not their definition. Notice the
difference between the class name and the object name: In the previous example,
CRectangle was the class name (i.e., the type), whereas rect was an object of type
CRectangle. It is the same relationship int and a have in the following declaration:
int a;
Class in C++
where ini is the type name (the class) and a is the variable name (the object).
After the previous declarations of CRectangle and reel, we can refer within the body
of the program to any of the public members of the object rect as if they were normal
functions or normal variables, just by putting the object’s name followed by a dot (.)
and then the name of the member. All very similar to what we did with plain data
structures before. For example:
rect.sel_values (3,4);
my area = rectareaf);
The only members of rect that we cannot access from the body of our program outside
the class are x and y, since they have private access and they can only be referred from
within other members of that same class.
Here is the complete example of class CRectangle:
//classes example area: 12
tfinclude <iostream>
using namespace std;
class CRectangle {
int x, y;
public:
void set_values (int,int);
ini area () {return (x*y);}
int main () {
CRectangle rect;
rect.set_values (3,4);
cout « “area: “ « rect.area();
return 0;
}
The most important new thing in this code is the operator of scope two colons)
included in the definition of set_values(). It is used to define a member of a class from
outside the class definition itself.
Zero to Mastery Microsoft Visual in C++
You may notice that the definition of the member function area() has been included
directly within the definition of the CRectangle class given its extreme simplicity,
whereas set_values() has only its prototype declared within the class, but its definition
is outside it. In this outside declaration, we must use the operator of scope (::) to
specify that we are defining a function that is a member of the class CRectangle and
not a regular global function.
The specifies the class to which the member being declared belongs, granting exactly
the same scope properties as if this function definition was directly included within
the class definition. For example, in the function set_values() of the previous code,
we have been able to use the variables x and y, which are private members of class
CRectangle, which means they are only accessible from other members of their class.
The only difference between defining a class member function completely within its
class or to include only the prototype and later its definition, is that in ihc first case the
function will automatically be considered an inline member function by the compiler,
while in the second it will be a normal (not-inline) class member function, which in
fact supposes no difference in behavior.
Members x and у have private access (remember that if nothing else is said, all members
of a class defined with keyword class have private access). By declaring them private
we deny access to them from anywhere outside the class. This makes sense, since we
have already defined a member function to set values for (hose members within the
object: the member function set_values(). Therefore, the rest of the program does not
need to have direct access to them. Perhaps in a so simple example as this, it is
difficult to see an utility in protecting those two variables, but in greater projects it
may be very important that values cannot be modified in an unexpected way (unexpected
from the point of view of ihc object).
One of the greater advantages of a class is that, as any other type, we can declare
several objects of it. For example, following with the previous example of class
CRectangle, we could have declared the object rectb in addition to the object red:
// example: one class, two objects rect area: 12
tfinclude <iostream> rectb area: 30
using namespace std;
class CRectangle {
int x, y;
public:
void set_valucs (int,int);
int area () {return (x*y);}
};
void CRectangle::set_values (int a, int b) {
Class in C++
x = a;
y = b;
ini main () {
CRectangle reel, rectb; rect.se Lvalues (3,4);
rectb.set_values (5,6);
cout « “rect area: “ « rect.areaO « endl;
cout « “rectb area: ** « rectb.area() « endl;
return 0;
In this concrete case, the class (type of the objects) to which we arc talking about is
CRectangle, of which there are two instances or objects: reel and rectb. Each one of
them has its own member variables and member functions.
Notice that the call to rect.areaf) does not give the same result as the call to rectb.areaQ.
This is because each object of class CRectangle has its own variables x and y, as they,
in some way, have also their own function members set_value() and area() that each
uses its object’s own variables to operate.
That is the basic concept of object-oriented programming: Data and functions are
both members of the object. We no longer use sets of global variables that we pass
from one function to another as parameters, but instead we handle objects that have
their own data and functions embedded as members. Notice that we have not had to
give any parameters in any of the calls to reel, area or rectb.area. Those member
functions directly used the data members of their respective objects rect and rectb.
Objects generally need to initialize variables or assign dynamic memory during their
process of creation to become operative and to avoid returning unexpected values
during their execution. For example, what would happen if in the previous example
we called the member function area() before having called function set_values()?
Zero to Mastery Microsoft Visual in C++
Probably we would have gotten an undetermined result since the members x and у
would have never been assigned a value.
In order to avoid that, a class can include a special function called constructor, which
is automatically called whenever a new object of this class is created This constructor
function must have the same name as the class, and cannot have any return type; not
even void.
We are going to implement CRectangle including a constructor:
// example: class constructor rect area: 12
^include <iostream> rectb area: 30
using namespace std;
class CRectangle {
int width, height;
public:
CRectangle (int,int);
int area () {return (width*height);}
int main () {
CRectangle rect (3,4);
CRectangle rectb (5,6);
cout « “rect area: “ « rect.areaQ « endl;
cout « “rectb area: “ « rectb.area() « endl;
return 0;
As you can see, the result of this example is identical to the previous one. But now we
have removed the member function set_values(), and have included instead a
constructor that performs a similar action: it initializes the values of x and у with the
parameters that are passed to it.
Notice how these arguments are passed to the constructor at the moment at which the
objects of this class are created:
CRectangle rect (3,4);
CRectangle rectb (5,6);
Class in C++
class CRectangle {
int *width, ^height;
public:
CRectangle (int,int);
-CRectangle ();
int area () {return (*width * ^height);}
CRectangle::-CRectangle () {
delete width;
delete height;
int main () {
CRectangle reel (3,4), rectb (5,6);
Zero to Mastery Microsoft Visual in C++
CRectangle: CRectangle () {
width = 5;
height = 5;
int main () {
CRectangle rect (3,4);
CRectangle rectb;
cout « “rect area: “ « rect.area() « endl;
cout « “rectb area: “ « rcctb.areaf) « endl;
return 0;
Class in C++
In this case, rectb was declared without any arguments, so it has been initialized with
the constructor that has no parameters, which initializes both width and height with a
value of 5.
Important: Notice how if we declare a new object and we want to use its default
constructor (the one without parameters), we do not include parentheses ():
CRectangle rectb; // right
CRectangle rectb(); // wrong!
The compiler assumes that CExample has a default constructor, so you can declare
objects of this class by simply declaring them without any arguments:
CExample ex;
But as soon as you declare your own constructor for a class, the compiler no longer
provides an implicit default constructor. So you have to declare all objects of that
class according to the constructor prototypes you defined for the class:
class CExample {
public:
int a,bTc;
CExample (int n, int m) { a=n; b=m; };
void multiply () ( c=a*b; };
Here we have declared a constructor that takes two parameters of type int. Therefore
the following object declaration would be correct:
CExample ex (2,3);
But,
CExample ex;
Zero to Mastery Microsoft Visual in C++
Would not be correct, since we have declared the class to have an explicit constructor,
thus replacing the default constructor.
But the compiler not only creates a default constructor for you if you do not specify
your own. It provides three special member functions in total that are implicitly
declared if you do not declare your own. These are the copy constructor, the copy
assignment operator, and the default destructor.
The copy constructor and the copy assignment operator copy all the data contained in
another object to the data members of the current object. For CExample, the copy
constructor implicitly declared by the compiler would be something similar to:
CExarnple::CExample (const CExample& rv) {
a=rv.a; b=rv.b; c=rv.c;
}
Therefore, the two following object declarations would be correct:
CExample ex (2,3);
CExample ex2 (ex); // copy constructor (data copied from ex)
private:
ini x;
public:
Class in C++
AO {A= 10;}
-AO {}
private:
char *name;
public:
BO
-BO
delete namelJ;
//Copy constructor
B(const В &b)
Let us imagine if you don’t have a copy constructor for the class B. At the first place,
if an object is created from some existing object, we cannot be sure that the memory
is allocated. Also, if the memory is deleted in destructor, the delete operator might be
called twice for the same memory location.
This is a major risk. One happy thing is, if the class is not so complex this will come
to the fore during development itself. But if the class is very complicated, then these
kind of errors will be difficult to track.
If you need a copy constructor, you also need a destructor and operator=
If you need a copy constructor, it’s because you need something like a deep copy, or
some other management of resources. Thus, it is almost certain that you will need a
destructor and override the assignment operator.
It is perfectly valid to create pointers that point to classes. We simply have to consider
that once declared, a class becomes a valid type, so we can use the class name as the
type for the pointer. For example,
CRectangle * prect;
is a pointer to an object of class CRectangle.
As it happened with data structures, in order to refer directly to a member of an object
pointed by a pointer we can use the arrow operator (->) of indirection. Here is an
example with some possible combinations:
// pointer to classes example
tfinclude <iostream>
using namespace std;
class CRectangle {
int width, height;
int main () {
CRectangle a, *b, *c;
CRectangle * d = new CRectangle[2];
b= new CRectangle;
c= &a;
a,set_vahies (1,2);
b->set_values (3,4);
d->set_values (5,6);
d{ 1 J-set_values (7,8);
cout « “a area: “ « a.areaQ « endl;
cout « “*b area: “ « b->area() « end];
cout « “*c area: “ « c->area() « endl;
cout « "d[OJ area: " « d[0].area() « endl;
cout « “d[lj area: “ « d[ lj>area() « endl;
deletelJ d;
delete b;
return 0;
Next you have a summary on how can you read some pointer and class operators
(*, &f ->, I J) that appear in the previous example:
expression can be read as
*x pointed by x
&x address of x
x+y member у of object x
x->y member у of object pointed by x
(*x).y member у of object pointed by x (equivalent to the previous one)
xLOJ first object pointed by x
xUJ second object pointed by x
xlnj (n+l)th object pointed by x
Class in C++
Be sure that you understand the logic under all of these expressions before proceeding
with the next sections. If you have doubts, read again this section and/or consult the
previous sections about pointers and data structures.
Classes can be de lined not only with keyword class, but also with keywords struct and
union. The concepts of class and data structure are so similar that both keywords
(struct and class) can be used in C++ to declare classes (Le., structs can also have
function members in C++, not only data members). The only difference between both
is that members of classes declared with the keyword struct have public access by
default, while members of classes declared with the keyword class have private access.
For all other purposes both keywords are equivalent. The concept of unions is different
from that of classes declared with struct and class, since unions only store one data
member at a time, but nevertheless they are also classes and can thus also hold function
members. The default access in union classes is public.
The keyword this represents a pointer to the object whose member function is being
executed. It is a pointer to the object itself.
One of its uses can be to check if a parameter passed to a member function is the
object itself. For example,
// this yes, &a is b
tfinclude <iostream>
using namespace std;
class CDummy {
public:
int isitme (CDummy& param);
};
int CDummy::isitme (CDummy& param)
CDummy* b = &a;
if ( b->isitme(a) )
cout« “yes, &a is b”;
return 0;
It is also frequently used in operator- member functions that return objects by reference
(avoiding the use of temporary objects). Following with the vector’s examples seen
before we could have written an operatoi- function similar to this one:
CVector& C Vector ::operator= (const CVector& param)
x=param.x;
у=p ar army;
return *this;
}
In fact, this function is very similar to the code that the compiler generates implicitly
for this class if we do not include an operator^ member function to copy objects of
this class.
A class can contain static members, cither data or functions. Static data members of
a class are also known as “class variables”, because there is only one unique value for
all the objects of that same class. Their content is not different from one object of this
class to another. For example, it may be used for a variable within a class that can
contain a counter with the number of objects of that class that are currently allocated,
as in the following example:
// static members in classes 7
^include <iostream> 6
using namespace std;
class CDummy {
public:
static int n;
CDummy () { n++; };
-CDummy () { n—; };
Class in C++
In fact, static members have the same properties as global variables but they enjoy
class scope. For that reason, and to avoid them to be declared several times, we can
only include the prototype (its declaration) in the class declaration but not its definition
(its initialization). In order to initialize a static data-member we must include a formal
definition outside the class, in the global scope, as in the previous example:
int CDummy::n=0;
Because it is a unique variable value for all the objects of the same class, it can be
referred to as a member of any object of that class or even directly by the class name
(of course this is only valid for static members):
cout « a.n;
cout« CDummy::n;
These two calls included in the previous example are referring to the same variable:
the static variable n within class CDummy shared by all objects of this class.
Once again, 1 remind you that in fact it is a global variable. The only difference is its
name and possible access restrictions outside its class.
Just as we may include static data within a class, we can also include static functions.
They represent the same: they are global functions that arc called as if they were
object members of a given class. They can only refer to static data, in no case to non
static members of the class, as well as they do not allow the use of the keyword this,
since it makes reference to an object pointer and these functions in fact are not members
of any object but direct members of the class.
Example:
The concept of inline functions:
^include <iostreani.h>
int demo(int);
void main( )
int x;
Class in C++
return 5*xl;
When the above program is written as normal function the compiled code would look
like below:
^include <iostream.h>
int demo(int);
void main( )
{
Zero to Mastery Microsoft Visual in C++
int x;
cout« “\n Enter the Input Value:”;
cin»x;
//Call is made to the function demo
cout«“\n The Output is:” « demo(x);
REVIEW EXERCISE
OVERLOADING IN
C++
C++ allows both functions and operators to be overloaded and hence it includes function
and operator overloading*
C++enables several functions of the same name to be defined, as long as these functions
have different sets of parameters (at least as far as their types are concerned). This
capability is called function overloading* When an overloaded function is called, the
C++ compiler selects the proper function by examining the number, types and order
of the arguments in the call* Function overloading is commonly used to create several
functions of the same name that perform similar tasks but on different data types*
//overloaded function
#include
void sum(int a,int b)
cout«A+B«ENDL;
cout«A+B+C«ENDL;
Zero to Mastery Microsoft Visual in C++
cout«A+B+C+D«ENDL;
}
void sum(int ajnt b,int cjnt d,int e)
cout«A+B+C+D+E«ENDL;
}
void mainQ
return (a*b);
return (a/b);
int main ()
int x=5,y=2;
float n=5.0,m=2.0;
Overloading in C++
return 0;
In this case we have defined two functions with the same name, operate, but one of
them accepts two parameters of type ini and the other one accepts them of type float
The compiler knows which one to call in each case by examining the types passed as
arguments when the function is called. If it is called with two ints as its arguments it
calls to the function that has two int parameters in its prototype and if it is called with
two floats it will call to the one which has two float parameters in its prototype.
In the first call to operate the two arguments passed arc of type int, therefore, the
function with the first prototype is called; This function returns the result of mu hiplying
both parameters. While the second call passes two arguments of type float, so the
function with the second prototype is called. This one has a different behavior: it
divides one parameter by the other. So the behavior of a call to operate depends on the
type of the arguments passed because the function has been overloaded.
Notice that a function cannot be overloaded only by its return type. At least one of its
parameters must have a different type.
int funefint i)
return i;
return i+j;
}
ini func(int i)
return i;
double funcfdouble i)
return i;
cout«func(10);
cout«func(10.201);
Overloading in C++
ini func(int i)
{
return i;
}
double func(int i)
{
return i;
}
No, because you can’t overload functions if they differ only in terms of the data type
they return.
It allows existing C++ operators to be redefined so that they work on objects of user-
defined classes. Overloaded operators are syntactic sugar for equivalent function calls.
They form a pleasant facade that doesn’t add anything fundamental to the language
(but they can improve understandability and reduce maintenance costs).
In computer programming, operator overloading (less commonly known as operator
ad-hoc polymorphism) is a specific case of polymorphism in which some or all of
operators like +, =, or == have different implementations depending on the types of
their arguments. Sometimes the overloadings are defined by the language; sometimes
the programmer can implement support for new types.
Operator overloading is useful because it allows the developer to program using notation
closer to the target domain and allows user types to look like types built into the
language. It can easily be emulated using function calls.
C++ incorporates the option to use standard operators to perform operations with
classes in addition to with fundamental types. For example,
a = b + c;
This is obviously valid code in C++, since the different variables of the addition arc
all fundamental types. Nevertheless, it is not so obvious that we could perform an
operation similar to the following one:
struct {
string product;
float price;
} a, b, c;
a = b + c;
Zero to Mastery Microsoft Visual in C++
In fact, this will cause a compilation error, since we have not defined the behavior our
class should have with addition operations. However, thanks to the C++ feature to
overload operators, we can design classes able to perform operations using standard
operators. Here is a list of all the operators that can be overloaded:
Overloadable operators
* /
temp.x = x + param.x;
temp, у = у + param.у;
return (temp);
int main () {
CVector a (3,1);
CVector b (1,2);
C Vector c;
c = a + b;
cout « c.x « « c.y;
return 0;
It may be a little confusing to see so many times the CVector identifier. But, consider
that some of them refer to the class name (type) CVector and some others are functions
with that name (constructors must have the same name as the class). Do not confuse
them:
CVector (int, int); // function name CVector (constructor)
CVector operator+ (CVector); // function returns a CVector
The function operator + of class CVector is the one that is in charge of overloading
the addition operator (+). This function can be called either implicitly using the
operator, or explicitly using the function name:
c = a + b;
c = a.operators (b);
Both expressions are equivalent.
Notice also that we have included the empty constructor (without parameters) and we
have defined it with an empty block:
CVector () { };
This is necessary, since we have explicitly declared another constructor:
CVector (int, int);
And when we explicitly declare any constructor, with any number of parameters, the
default constructor with no parameters that the compiler can declare automatically is
not declared, so we need to declare it ourselves in order to be able to construct objects
of this type without parameters. Otherwise, the declaration:
CVector c;
included in main() would not have been valid.
Zero to Mastery Microsoft Visual in C++
Anyway, 1 have to warn you that an empty block is a bad implementation for a
constructor, since it does not fulfill the minimum functionality that is generally expected
from a constructor, which is the initialization of all the member variables in its class.
In our case, this constructor leaves the variables x and у undefined. Therefore, a more
advisable definition would have been something similar to this:
CVector () ( x=0; y=0; };
which in order to simplify and show only the point of the code 1 have not included in
the example.
As well as a class includes a default constructor and a copy constructor even if they
are not declared, it also includes a default definition for the assignment operator (=)
with the class itself as parameter. The behavior which is defined by default is to copy
the whole content of the data members of the object passed as argument (the one at the
right side of the sign) to the one at the left side:
CVector d (2,3);
CVector e;
e = d; // copy assignment operator
The copy assignment operator function is the only operator member function
implemented by default. Of course, you can redefine it to any other functionality that
you want, like for example, copy only certain class members or perform additional
initialization procedures.
The о ve r I oad оf ope ra to rs doe s n ot fo rce i ts operat i о n to bea r a re 1 a t i о n to th e m athe m ati ca I
or usual meaning of the operator, although it is recommended. For example, the code
may not be very intuitive if you use operator + to subtract two classes or operator== to
fill with zeros a class, although it is perfectly possible to do so.
Although the prototype of a function operator+ can seem obvious since it takes what
is at the right side of the operator as the parameter for the operator member function
of the object at its left side, other operators may not be so obvious. Here you have a
table with a summary on how the different operator functions have to be declared
(replace @ by the operator in each case):
myclass temp;
// add the data of the object
// that generated the call
// with the data of the object
// passed to it and store in temp
temp.subl=subl +ob.subl;
temp.sub2=sub2 + ob.sub2;
return temp;
Zero to Mastery Microsoft Visual in C++
void main()
{
myclass ob 1(10,90);
myclass ob2(90,10);
// this is valid
obl=obl+ob2;
obLshow();
double real,
imag;
public:
complx( double real = 0., double imag = 0.); // constructor
complx operator+(const complx&) const; // operator+()
// define constructor
complx: :complx( double r, double i )
{
real = r; imag = i;
complx result;
result.real = (thisoreal + c.real);
re suit, imag = (this->imag + c.imag);
return result;
Overloading in C++
int mainf)
{
complx x(4,4);
complx y(6,6);
complx z = x + y; // calls complx: :operator+()
}
Make sure that the members inserted are separated by a tab, newline or space so that
they appear as if they were concatenated when displayed on the screen. Remember
also to place the endl manipulator at the end of the insertion chain to force a buffer
flush* Finally, the overloaded operator should return the ostream object after the
members have been inserted to it This will enable you to chain several objects in a
single cout statement:
student si, s2;
cout«sl«s2; //chaining multiple objects
The insertion operations and the return statement can be accomplished in a single
statement:
ostream& operator« (ostream& os, const student^ s)
{
return os«s.get_name()« V«s.get_department()«endl;
}
Now you can use the overloaded «in your code:
int mainO
{
student st(“Bill Jones”. “Zoology”);
cout«st;
}
As expected, this program displays:
Bill Jones Zoology
struct Y {
void operator’O {
cout « “void Y::operator!()“ « endl;
Overloading in C++
struct Z { };
ini mainQ {
X ox; Y oy; Z oz;
lox;
loy;
H ’oz;
class Y { };
// non-member prefix ++y
void operator++( Y&) ( }
ini mainQ {
X x;
Yy;
// calls x.operator++()
x.operator++();
// calls operator-H-(y)
++y;
// explicit calk like ++y
operator++(y);
}
The postfix increment operator ++ can be overloaded for a class type by declaring a
non-member function operator opcrator++() with two arguments, the first having
class type and the second having type int Alternatively, you can declare a member
function operator operator++() with one argument having type int. The compiler uses
the int argument to distinguish between the prefix and postfix increment operators.
For implicit calls, the default value is zero.
For example:
class X {
public:
// member postfix x++
void operater++(int) { };
class Y { };
// non-member postfix y++
void operator++(Y&, int) { };
int main() {
X x;
Yy;
// calls x.operator++(0)
// default argument of zero is supplied by compiler
x++;
// explicit call to member postfix x++
x.opcralor++(0);
// calls operator++(y, 0)
y++;
// explicit call to non-member postfix y++
opcrator++(y, 0);
}
The prefix and postfix decrement operators follow the same rules as their increment
counterparts.
Overloading in C++
int main() {
X xl, x2;
xl = x2; // call xl.operator=(x2)
xl =5; // call xl.operator=(5)
struct В : A {
B& operator=(char) {
cout « “B& B::operator=(char)” « endl;
return *this;
struct С : В { };
int main() {
В bl;
В b2;
A* apl = &bl;
A* ap2 = &bl;
*apl = ‘z’;
*ap2 = b2;
C cl;
// cl = V;
REVIEW EXERCISE
A key feature of C++ classes is inheritance. Inheritance allows to create classes which
are derived from other classes, so that they automatically include some of its “parent’s”
members, plus its own. Inheritance is the process by which new classes called derived
classes are created from existing classes called base classes. The derived classes have
all the features of the base class and the programmer can choose to add new features
specific to the newly created derived class.
For example, a programmer can create a base class named fruit and define derived
classes as mango, orange, banana, etc. Each of these derived classes, (mango, orange,
banana, etc.) has all ihc features of the base class (fruit) with additional attributes or
features specific to these newly created derived classes. Mango would have its own
defined features, orange would have its own defined features, banana would have its
own defined features, etc.
Classes that are derived from others inherit all the accessible members of the base
class. That means that if a base class includes a member A and we derive it to another
class with another member called B, the derived class will contain both members A
and B.
In order to derive a class from another, we use a colon (:) in the declaration of the
derived class using the following format:
class derived_class_name: public base_class_name
{ /*.„*/ };
Inheritance, Polymorphism & Virtual Functions
Figure 4.1
The class CPolygon wou I d co nta i n me mbers that are com mon for both types of polygon.
In our case, width and height. And CRectangle and CTriangle would be its derived
classes, with specific features that are different from one type of polygon to the other.
// derived classes
tfinclude <iostream>
using namespace std;
class CPolygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b;}
Zero to Mastery Microsoft Visual in C++
int main () {
CRectangle reel;
CTriangle trgl;
recl.set_values (4,5);
trgl. set_ values (4,5);
cout « recLarea() « endl;
cout « trgLarea() « endl;
return 0;
The objects of the classes CRectangle and CTriangle each contain members inherited
from CPolygon. These are: width, height and set_values().
The protected access specifier is similar to private. Its only difference occurs in fact
with inheritance. When a class inherits from another one, the members of the derived
class can access the protected members inherited from the base class, but not its private
members.
Since we wanted width and height to be accessible from members of the derived
classes CRectangle and CTriangle and not only by members of CPolygon, we have
used protected access instead of private.
We can summarize the different access types according to who can access them in the
following way:
Access public protected private
members of the same class yes yes yes
members of derived classes yes yes no
not members yes no no
Inheritance, Polymorphism & Virtual Functions
Where "not members” represent any access from outside the class, such as from main(),
from another class or from a function.
In our example, the members inherited by CRectangle and CTriangle have the same
access permissions as they had in their base class CPolygon:
CPolygon:: width // protected access
CRectangle::width // protected access
Although the constructors and destructors of the base class are not inherited themselves,
its default constructor (Le+, its constructor with no parameters) and its destructor are
always called when a new object of a derived class is created or destroyed.
If the base class has no default constructor or you want that an overloaded constructor
is called when a new derived object is created, you can specify it in each constructor
definition of the derived class:
derived_constructor_name (parameters): base_constructor_name (parameters) {...}
For example:
//constructors and derived classes
tfinclude <iostream>
using namespace std;
class mother {
public:
mother ()
{ cout « “mother: no parameters\rT; }
mother (int a)
{ cout « “mother: int parametefVn”; }
};
class daughter : public mother {
daughter (int a)
{ cout « “daughter: int parameter\n\n”; }
};
class son : public mother {
int main () {
daughter cynthia (0);
son daniel(O);
return 0;
Inheritance, Polymorphism & Virtual Functions
Notice the difference between which mother’s constructor is called when a new daughter
object is created and which when it is a son object. The difference is because the
constructor declaration of daughter and son:
daughter (int a) // nothing specified: call default
son (int a): mother (a) // constructor specified: call this
Reusability: Inheritance helps the code to be reused in many situations. The base
class is defined and once it is compiled, it need not be reworked. Using the concept of
inheritance, the programmer can create as many derived classes from the base class as
needed while adding specific features to each derived class as needed.
Saves Time and Effort: The above concept of reusability achieved by inheritance
saves the programmer time and effort. Since the main code written can be reused in
various situations as needed.
There are some points to be remembered about C++ inheritance. The protected and
public variables or members of the base class are all accessible in the derived class.
But a private member variable not accessible by a derived class. It is a well known
fact that the private and protected members are not accessible outside the class. But a
derived class is given access to protected members of the base class.
Let us see a piece of sample code for C++ inheritance. The sample code considers a
class named vehicle with two properties to it, namely color and the number of wheels.
A vehicle is a generic term and it can later be extended to any moving vehicles like
car, bike, bus etc.
class vehicle //Sample base class for C++ inheritance
protected:
char colornamcflOJ;
int number_of_w heels;
public:
vehiclef);
-vehiclef);
void start();
void stop();
class Car: public vehicle //Sample derived class for C++ inheritance
protected:
char type_of_fuel;
public:
Car();
The derived class Car will have access to the protected members of the base class. It
can also use the functions start, stop and run provided ihe functionalities remain the
same. In ease the derived class needs some different functionalities for the same
functions start, stop and run, then the base class should implement the concept of
virtual functions.
Inheritance Example:
class sample
public:
Inheritance, Polymorphism & Virtual Functions
sample(void) { x=0; }
void f(int nl)
x= nI*5;
public:
sample(void) ( sl=0; }
void fl(int nl)
{
sl=nl*10;
void output(void)
{
sample ::output();
coul «si;
private:
int si;
int mam(void)
sample s;
s.f(IO);
s.output();
s.fl(20);
s.outputQ;
In the above example, the derived class is sample and the base class is sample. The
derived class defined above has access to all public and private variables. Derived
classes cannot have access to base class constructors and destructors. The derived class
would be able to add new member functions, or variables, or new constructors or new
destructors. In the above example, the derived class sample has new member function
fl() added in it. The line:
sample s;
creates a derived class object named as s. When this is created, space is allocated for
the data members inherited from the base class sample and space is additionally allocated
lor the data members defined in the derived class sample.
The base class cons true tor sample is used to initialize the base class data members and
the derived class constructor sample is used to initialize the data members defined in
derived class.
The access specifier specified in the line:
class sample: public sample
Public indicates that the public data members which are inherited from the base class
by the derived class sample remains public in the derived class.
C++ distinguishes two types of inheritance: public and private. As a default, classes
are privately derived from each other. Consequently, we must explicitly tell the compiler
to use public inheritance.
The type of inheritance influences the access rights to elements of the various
superclasses. Using public inheritance, everything which is declared private in a
superclass remains private in the subclass. Similarly, everything which is public remains
public. When using private inheritance the things are quite different as is shown in
table below.
in subclasses but which should not be accessible from the outside. Thus, one could say
elements of this type are between private and public elements in that they can be used
within the class hierarchy rooted by the corresponding class.
Inheritance in C++can also be classified as Single, Multiple, multilevel, Hierarchical,
multipath and Hybrid.
Bock
t
Paperback Book
Note the progression from general to specific in the figure* Another common attribute
found in the design of most class hierarchies is that the derived class has a “kind of’
relationship with the base class* In the figure, a Book is a kind of a PrintedDocument,
and a PaperbackBook is a kind of a book*
class abc //example of single inheritance
protected:
hit x;
int y;
};
class def:abc
private:
int z;
public:
void displayO
x=10;
y=20;
z=x+y;
cout«z«“
Inheritance, Polymorphism & Virtual Functions
void mainQ
class def o;
o.displayO;
getch();
public :\
computerfchar *, ini, float,
char*, long, int,
ini. int, int, int);
void show_computer(void);
private:
char name[64];
int hard_disk; //size of
float floppy;
protected:
int rollno;
char *name;
public:
void getdata(int b,char *n)
{
rollno = b;
name = n;
void putdata
(void)
cout< < " The Name Of Student \t:** < < name< < endl;
cout< < M The Roll No. Is \t:” < < rollno< < endl;
protected:
float nil,m2;
public:
void gettest(float b,float c)
ml = b;
m2 = c;
in C++
void puttest(void)
protected:
float total;
public:
void display result(void)
total = ml + m2;
putdata();
puttestf);
cout< < “ Total ОГ The Two \t: “< < total< < endl;
void main()
clrscrQ;
int x;
float y,z;
char n[20];
cout< < “Enter Your Name:”;
cin»n;
cout< < “Enter The Roll Number:”;
cin»x;
result rl;
rl*getdata(x,n);
cout< < “ENTER COMPUTER PROGRAMMING MARKS
cin»y;
cout< < “ENTER DRAWING MARKS:”;
cin»z;
Inheritance, Polymorphism & Virtual Functions
5
rLgettest(y,z);
cout< < endlc < endlc < “************ RESULT **************”< <
end];
rLdisplayresultO;
coutc < “**********************************”< < end!'
getchQ;
}
OUTPUT ************
Enter Your Name:Lionel
Enter The Roll Number:44
ENTER COMPUTER PROGRAMMING MARKS:95
ENTER DRAWING MARKS :90
result **************
The Name Of Student: Lionel
The Roll No. Is : 44
Marks In CP Is : 95
Marks In Drawing Is: 90
Total Of The Two : 185
public:
person(inttchar,string);
person();
int id();
Zero to Mastery Microsoft Visual in C++
char sex();
string name();
int changename(string);
static int resel_counl();
protected:
int perso n_id;
char person_sex;
siring person_narne;
static int count;
public:
student (string,char);
student (string,char,char);
int numbcrO;
char specializationO;
protected:
int student_number;
char student_specialization;
public:
employee (int,char);
employee (string,char,char);
int number ();
char working ();
protected:
int employee_number;
char employee_working;
ini person::count;
int person: :reset_count() {
person: :count=0;
Inheritance, Polymorphism & Virtual Functions
person_id=person::count;
person: :count++;
person_sex=b;
person_name=c;
person: :person()
person_id=person “count;
person: :count++;
char this_persons_sex;
int random=rand()%2;
if (random==0) this_persons_sex=’m’;
else this_persons_sex=T;
person_sex=this_persons_sex;
person_name=
int person::id()
return (person_id);
char person::sex()
return (person_sex);
string person::name()
return (person_name);
int person::changename(string a)
person_naine=a;
Zero to Mastery Microsoft Visual in C++
return 0;
student_numbei-person_id;
person_name=a;
student_specialization=b;
person_name=a;
stu de n t_n u m be r=perso n_i d;
student_specialization=b;
person__sex=c;
return (student_number);
return (student_specialization);
employee_numbei-a;
employee_working=b;
person_name=a;
emp]oyee_number=person_id;
person_sex=b;
employ ee_working=c;
Inheritance, Polymorphism & Virtual Functions
int employee::number()
return (employee_number);
return (employee_working);
int mainQ
person: :reset_count();
srand(tirne(NULL));
employee ** emp;
student ** stu;
char tmpsex;
string tmpname;
string junk;
char tmpspec;
emp=new employee1^ 10J;
stu=new student*[10];
cout«“students:”«endl;
for (int i=0;i<10;i++)
cout«“name:”;
getline(cinTtmpname);\
cout«“specialization:
cin »tmpspec;
cout«“sex:”;
cin » tmpsex;
getline(cinjunk); // clear input buffer from junk cin leaves there
stu[i]=new studcnt(tmpnamedmpspec,tmpsex);
cout«“employees:”«endl;
for (int i=0;i<10;i++)
Zero to Mastery Microsoft Visual in C++
cout«“name:”;
getline(cin,tmpname);
cout«“sex:”;
cm» tmpsex;
cout« “Working?:”;
cin »tmpspec;
getline(cin junk);
emp[i]=new employee(tmpname,tmpsex,tmpspec);
1
cout« “Student Data:\nid\tname\tsex\tspecialization\n”:
cout«“ ■”«endl;
for (int i =0; i<10;++i)
I
cout«stu[i]->id()« “\t”«stu[i]->name()«“\t”«stu[i]->sex()«“\t”«stu[i]
>specialization()«endl;
}
cout«“------------------------------------------------------------------------ ”«endl;
cout« “\nEmployee Data:\nid\tname\tsex\tworking\n”;
cout«“------------------------------------------------------------------------ ”«endl;
for (int i =0; i<10;++i)
{
cout<<emp[ij->id()<<“\t”<<emp[i]->name()<<“\t”<<ernp[i]
>sexO«“\t”«empLiJ->workingO«cndl;
}
cout«“------------------------------------- ”«endl;
coul«“\n\nPress <ENTER> to Exit
cin.getQ;
return 0;
4.5.1 Introduction
Polymorphism is the ability to use an operator or function in different ways
Polymorphism gives different meanings or functions to the operators or functions
Inheritance, Polymorphism & Virtual Functions
Poly, referring to many, signifies the many uses of these operators and functions. A
single function usage or an operator functioning in many ways can be called
polymorphism. Polymorphism refers to codes, operations or objects that behave
differently in different contexts.
Polymorphism is a powerful feature of the object oriented programming language
C++. A single operator + behaves differently in different contexts such as integer,
float or strings referring the concept of polymorphism. The above concept leads to
operator overloading. The concept of overloading is also a branch of polymorphism.
When the exiting operator or function operates on new data type it is overloaded. This
feature of polymorphism leads to the concept of virtual methods.
Polymorphism refers to the ability to call different functions by using only one type
of function call. Suppose a programmer wants to code vehicles of different shapes
such as circles, squares, rectangles, etc. One way to define each of these classes is to
have a member function for each that makes vehicles of each shape. Another convenient
approach the programmer can take is to define a base class named Shape and then
create an instance of that class. The programmer can have array that hold pointers to
all different objects of the vehicle followed by a simple loop structure to make the
vehicle, as per the shape desired, by inserting pointers into the defined array. This
approach leads to different functions executed by the same function call. Polymorphism
is used to give different meanings to the same concept. This is the basis for Virtual
function implementation.
In polymorphism, a single function or an operator functioning in many ways depends
upon the usage to function properly. In order for this to occur, the following conditions
must apply:
• All different classes must be derived from a single base class. In the above
example, the shapes of vehicles (circle, triangle, rectangle) are from the single
base class called Shape.
• The member function must be declared virtual in the base class. In the above
example, the member function for making the vehicle should be made as virtual
to the base class.
public:
virtual void displayO
{
cout«‘*\nBase”;
};
class derived : public base
Zero to Mastery Microsoft Visual in C++
public:
void displayO
cout«"\nDerived”;
};
void main()
In the above example, the pointer is of type base but it points to the derived class
object. The method displayO is virtual in nature. Hence in order to resolve the virtual
method call, the context of the pointer is considered, Le., the display method of the
derived class is called and not that of the base. IГ the method was non virtual in nature,
the displayO method of the base class would have been called.
public:
~base()
I;
class derived : public base
Inheritance, Polymorphism & Virtual Functions
public:
~derived()
{
};
void mainQ
In this case the type of the pointer would be considered. Hence as the pointer is of
type base, the base class destructor would be called but I he derived class destructor
would not be called at all. The result is memory leak. In order to avoid this, we have
to make the destructor virtual in the base class. This is shown in the example below:
^include <ioslream.h>
class base
{
public:
virtual -baseQ
};
class derived : public base
{
public:
-derivedf)
{
}
I;
void mainQ
{
base *ptr = new derivedf);
// some code
delete ptr;
Zero to Mastery Microsoft Visual in C++
};
class CommandButton : public Window
public:
void Create()
pointers to the functions from each of the objects of the derived class. Whenever there
is a function call made to the C++ virtual function, the v-table is used to resolve to the
function address. This is how the Dynamic binding happens during a virtual function
call.
#include <string.h>
#include <assert.h>
^include <iostream.h>
typedef double Coord;
/*
The type of X/Y points on the screen.
*/
enum Color (Co_red, Co_greent Co_blue);
/*
Colors.
*/
// abstract base class for all shape types
class Shape {
protected:
Coord xorig; // X origin
Coord yorig; // Y origin
Color co; II color
/*
These are protected so that they can be accessed by derived classes. Private wouldn’t
allow this.
These data members are common to all shape types.
*/
public:
Shape(Coord x, Coord y, Color c):
xorig(x), yorig(y), co(c) {} // constructor
/*
Constructor to initialize data members common to all shape types.
*/
virtual ~Shape() {} II virtual destructor
/*
88 Zero to Mastery Microsoft Visual in C++
/*
Draw a line.
*/
};
И circle with radius
class Circle : public Shape {
Coord rad; // radius of circle
/*
Radius of circle.
* 1
public:
Circle(Coord x, Coord y, Color c, Coord r) : rad(r),
Shapefx, yt c) {} // constructor with base initialization
—Circlet) {cout « “~Circle\n”;} // virtual destructor
void draw() // virtual draw function
(
cout « “Circle” «
cout « xorig « « yorig « « int(co);
cout « « rad;
cout« “)\n”;
};
// text with characters given
class Text: public Shape {
char* str; // copy of string
public:
Text(Coord x, Coord y, Color cT const char* s) :
Shape(xt y, c) // constructor with base initialization
Zero to Mastery Microsoft Visual in C++
};
int main()
const int N = 5;
int i;
Shape* sptrs{NJ;
Pointer to vector of Shape* pointers. Pointers to classes derived from Shape can be
assigned to Shape* pointers.
*/
// initialize set of Shape object pointers
sptrsfOJ = new Line(0.1,0.1, Co_blue, 0.4, 0.5);
sptrsLl] = new Line(0.3, 0.2, Co_red, 0.9, 0.75);
sptrs[2] = new Circle(0.5, 0.5, Co_green, 0.3);
Inheritance, Polymorphism & Virtual Functions
public:
virtual void virtualfunctionamef) = 0 //This denotes the pure virtual function in C++
The other concept of pure virtual function remains the same as described in the previous
section of virtual function. To understand the declaration and usage of Pure Virtual
Function, refer to this example:
class Exforsys
public:
Inheritance, Polymorphism & Virtual Functions
public:
void example()
cout«"Welcome";
}
};
class Exf2:public Exforsys
public:
void example()
{
cout«“To Training":
};
void mainQ
{
Exforsys* arra[2];
Exfl el;
Exf2 e2;
arra[0]=⪙
arra[l]=&e2;
arra[O]->example();
arra[l ]->example();
Since the above example has no body, the pure virtual function examplef) is declared
with notation =0 in the base class Exforsys, The two derived class named Exfl and
Exf2 are derived from the base class Exforsys, The pure virtual function exampleQ
takes up new definition. In the main function, a list of pointers is defined to the base
class.
Two objects named el and e2 are defined for derived classes Exfl and Exf2, The
address of the objects el and e2 are stored in the array pointers which are then used
for accessing the pure virtual function exampleQ belonging to both the derived class
EXfl and EXf2 and thus, the output is as in the above example.
Zero to Mastery Microsoft Visual in C++
The programmer must clearly understand the concept of pure virtual functions having
no body in the base class and the notation =0 is independent of value assignment. The
notation =0 simply indicates the Virtual function is a pure virtual function as it has no
body.
Some programmers might want to remove this pure virtual function from the base
class as it has no body but this would result in an error. Without the declaration of the
pure virtual function in the base class, accessing statements of the pure virtual function
such as, arra[0]->example() and arra[l]->example() would result in an error. The
pointers should point to the base class Exforsys. Special care must be taken not to
remove the statement of declaration of the pure virtual function in the base class.
Training
In the above example, there are two derived classes Exfl and Exf2 from the base class
Exforsys. As shown in the above diagram, the Training class is derived from both of
the derived classes Ex 11 and Exf2* In this scenario, if a user has a member function in
the class Training where the user wants to access the data or member functions of the
class Exforsys it would result in error if it is performed like this:
class Exforsys
protected:
int x;
};
class Exfl :public Exforsys
{ I;
class Exf2:public Exforsys
I I;
class Training:public Exfl,public Exf2
Inheritance, Polymorphism & Virtual Functions
public:
ini exampleQ
return x;
The above program results in a compile lime error as the member function exampleQ
of class Training tries to access member data x of class Exforsys. This results in an
error because the derived classes Exfl and Exf2 (derived from base class Exforsys)
create copies of Exforsys called sub objects.
This means that each of the subobjects have Exforsys member data and member
functions and each have one copy of member data x. When the member function of
the class Training tries to access member data x, confusion arises as lo which of the
two copies it must access since it derived from both derived classes, resulting in a
compile time error
When this occurs, Virtual base class is used. Both of the derived classes Exfl and
Exf2 are created as virtual base classes, meaning they should share a common subobject
in their base class.
For Example:
class Exforsys
protected:
int x;
public:
ini exampleQ
return x;
};
In the above example, both Exfl and Exf2 arc created as Virtual base class
Zero to Mastery Microsoft Visual in C++
REVIEW EXERCISE
GETTING STARTED
WITH VISUAL C++ 6
This chapter will leach you how io create a project in version six of Visual C++* This
version of Microsoft’s C++ IDE has probably helped millions of developers in their
C++ Programming over the last 8 years* Microsoft Visual C++ has existed in many
versions for over 13 years on the Win 32 platform* Version 6 is the last non .NET
version and probably the most popular* It’s been around since 1999 and has had six
service packs*
In this chapter, you’ll learn how to create a Command Line project, add some source
code and then make it*
Figure 5.1
Zero to Mastery Microsoft Visual in C++
We’ll begin by creating a new Project. Visual C++ includes the AppWizard. This is a
Wizard that does all the donkey-work of creating project files for you. You should get
in the habit of using this as it saves a lot of time.
After starting the IDE, From the File menu click New and the New Dialog will
popup. Select “Win 32 Console Application” (Red Circle I in the image), then enter
a Project name (Red Circle 2 in the image) like Example L Now select somewhere for
the project files by clicking the location selector to the right of the Location: edit box
and Press OK.
Figure 5.2
Click the third radio button which says A “Hello, World” application, then click the
Finish button. Press Ok on the next page and your workspace panel will now show
Example I Files, with folders for Source Files, Header Files, and Resource files
(There will be none) and a ReadMe.txt with a summary of the project files.
Getting Started with Visual C++ 6
Figure 5.3
This is standard C++ file. In fact it’s also a standard C as well though it defaults to
cpp. You can mix cpp and c files but don't give them the same name as the compiler
will expect to compile both example Lc and example I .cpp into examplel .obj and it
will object to having two files generate the same object file.
To remove files from the Project, just select each in the tree and press delete. To add
a file right click on “Source Files" (for .cpp, or .c) or “Header Files" for .h and click
“add Hies to Folder". This will open a window so you can browse to your file, select
and add it.
Click on the Project name in the Workspace tree and press the F7 key. That will Make
the Hello World application. You can run it by pressing F5 but you won’t see much,
as it’s a console application and the window will open and close very quickly.
You need to get to the command line (Click the Start button, then click Run, type emd
and press enter) and navigate to the folder where the project files are located. After
compiling, a debug folder is created there and this contains the debug executable
which you can run.
Figure 5.4
Getting Started with Visual C++ 6
1 101
When developing a program, it’s obviously important to be able to debug it However
for release you want to provide as small an executable as possible, unbloated by debug
code. Here's how to do that
Click "Build” on the main Menu, then "Set Active Configuration” on the drop-down
menu. This opens a dialog that shows you all possible build configurations. Just switch
to the "Release” configuration (that’s the selected configuration in the picture), press
Ok and then do another build. This creates a release folder containing the release
executable. For a simple "Hello World” Application, the debug executable is 169 KB
in size. The release exe is 40KB.
You aren’t just limited to these two configurations either. Click “Build” on the main
menu then "Configurations”. This is where you add extra configurations. For example,
a project for one customer may include additional functionality, perhaps implemented
in an extra dlL This is where you create that configuration. You can then customize it
in the Settings dialog. (Click "Project” on the Main menu then "Settings” on the drop
down).
Figure 5.5
Settings
This dialog is probably the most complex in Visual C++ 6. The defaults are good
enough for many applications but there will come a time when you have to modify it.
Here is few examples. For example ALT-F7 is the keyboard shortcut to open the
Settings dialog.
Zero to Mastery Microsoft Visual in C++
The settings tree control lets you create settings for different configurations. Choose
a Configuration in the Combo. If you have common folders for resources then Choose
“All Configurations” and click the “Resources Tab”. Add one or more paths, separated
by semi-colons to the Additional resource include directories.
The Project Options at the bottom of the first four settings tabs (C/C++, 1 Ink, Resources,
Browse Info) show a summary of the options set by the controls on that tab. You can
edit these directly or select the tab controls. For example Select the Link tab and
scroll down the Project Options until you see /out “Debug/examplel.exe” at the
bottom line. Now select the p in example and delete it. You’ll seethe output file name
edit box update to reflect this.
Most of the time you don’t need to change the settings. Those that you are most likely
to do will be specifying extra paths for include files (Select Preprocessor on the
category combo on the C/C++ tab) and Resources as described above. For the rest, if
you don’t need to change them, don’t!
Figure 5.6
Getting Started with Visual C++ 6
Visual C++ has a powerful debugger that’s very easy to use. Let’s step through our
example program. To make it more interesting we’ll add an int variable and watch it
in a for loop.
Before the line *std::couf, add the following two lines of code.
for (int i=0;i < 5; i++)
std::cout « "i =” « i « "\n\n”;
Select the first line (for int i...) and press F9. This puts a breakpoint there- a red circle
in the margin. Now press F5 to start the debugging. You can exit the debugger at any
time by pressing Shift + F5.
Without the breakpoint, the program would immediately run to completion and stop.
Alternatively you can start a program by pressing either F10 or Fl 1.
You should see the three windows numbered 1 -3, in the picture above. I Shows local
variables, 2 shows the calling stack (for functions) and 3 shows variables you decide
to watch. If you can’t see these windows, Click "View” on the Menu then "Debug
Windows” and then "Watch”, "Calling Stack” or "variables” on the sub-menu.
When the program breaks at the for (int i =0;.*. line, the variables windows shows
that i has a nonsense value, like -858993460. This has just picked up whatever was in
RAM at the address of i. As soon as the loop starts executing, i takes the value 0. Press
Fit) to step through execution line by line.
Figure 5.7
Zero to Mastery Microsoft Visual in C++
REVIEW EXERCISE
GENERATING A WINDOWS
GUI PROGRAM 6
6.1 PROGRAMMING FOR THE WINDOWS GUI
Microsoft Visual C++ provides several different pathways for writing Windows GUI
programs. First, you can write GUI programs in C or C++ by directly calling the
functions provided by the underlying Win32 application program interface (API).
Using this approach. however, you must write many lines of routine code before you
can begin to focus on the tasks specific to your application. Second, you can write
Windows GUI programs in C++ using the Microsoft Foundation Classes. The MFC
provides a large collection of prewritten classes, as well as supporting code, which
can handle many standard Windows programming tasks, such as creating windows
and processing messages. You can also use the MFC to quickly add sophisticated
features to your programs, such as toolbars, split window views, and OLE support.
And you can use it to create ActiveX controls, which are reusable software components
that can be displayed in Web browsers and other container applications. The MFC can
simplify your GUI programs and make your programming job considerably easier.
Note that the MFC functions internally call Win32 API functions. The MFC is thus
said to "wrap" the Win32 API, providing a higher-level, more portable programming
interface. (In MFC programs, you’re also free to directly call Win32 API functions,
so you don’t lose their capabilities by choosing to use the MFC.)
Third, you can write Windows GUI programs in C++ using both the MFC and the
Microsoft Wizards. You can use AppWizard to generate the basic source files for a
Generating a Windows GUI Program
107
variety of different types of GUI programs. You can then use the ClassWizard tool to
generate much of the routine code required to derive classes, to define member functions
for processing messages or customizing the behavior of the MFC, to manage dialog
boxes, and to accomplish other tasks. The code generated by I he Wizards makes full
use of the MFC,
Note that the Wizards aren’t limited to generating simple program shells, but rather
can be used to produce programs containing extensive collections of advanced features,
including toolbars, status windows, context-sensitive online help, OLE support, database
access, and complete menus with partially or fully functional commands for opening
and saving files, printing, print previewing, and performing other tasks. Once you’ve
used the Wizards to generate the basic program source code, you can immediately
begin adding code specific to the logic of your program. Using this third approach,
you benefit not only from the prewritten code in the MFC, but also from the generated
source code that wses the MFC and handles many routine programming tasks. The
MFC and the Wizards free you from much of the effort required in creating your
program’s visual interface, and also help ensure that this interface conforms to
Microsoft’s guidelines.
In this section you’ll create a program named WinGreet, which is an example of the
simplest type of program that you can generate using AppWizard. You'll first generate
the program source code, then make several modifications to the generated code, and
finally build and run the program.
4* Type the name WinGreel into the Project Name: text box* This will cause
Visual C++ to assign the name WinGreet to the new project (as well as to the
project workspace that contains this project)*
5* In the Location: text box, specify the path of the folder to contain the project
files (that is, the project folder). If you wish, you can simply accept the default
folder that is initially contained in this box (the default folder is given the same
name as the project workspace, WinGreet). Click the button with the ellipsis
(*..) if you want to search for a different location. If the specified project folder
doesn’t exist, the Developer Studio will create it (it will also create the -Res
subfolder within the project folder to store several resource files, in addition to
one or more output subfolders)*
6* To complete the Projects tab of the New dialog box, make sure that the Win32
item is checked in the Platforms: area* Unless you've installed a cross-development
edition of Visual C++, Win32 will be the only option in this area*
7* Click the OK button in the New dialog box* The first of the AppWizard dialog
boxes, which is labeled “MFC AppWizard - Step I,” will now be displayed* In
the following descriptions of the AppWizard options that need to be selected,
the expression “(default)” follows the description of each option initially selected*
For these options, you need only make sure that you don’t change them*
8* In the Step I dialog box, select the Single Document application type, make
sure the Document/View Architecture Support option (default) is checked, and
select the English language (default)*
Choosing the Single Document application type causes AppWizard to generate a
single document interface (SDI) application, which is designed to display only
one document at a time. The Document/View Architecture Support option causes
AppWizard to generate separate classes for storing and for viewing your program’s
data, as well as to provide code for reading and writing the data from disk*
Finally, AppWizard will use the selected language for the program menu captions
and for the standard messages that the program displays* Click the Next > button
to display the Step 2 dialog box.
9* In the Step 2 dialog box, select the None item (default) to exclude database
support from the program*
Note that in any of the AppWizard dialog boxes (from Step 2 on) you can click
the < Back button to return to a previous step to review and possibly revise your
choices* Also, you can click the Finish button to skip the remaining dialog boxes
and immediately generate the program source code using the default values for
all choices in the remaining dialog boxes (don't click this button for the current
exercise)* And finally, you can click the button in the upper-right corner and
then click a control in the dialog box to obtain information on the related option*
Click the Next > button to reveal the Step 3 dialog box*
Generating a Windows GUI Program
109
10. In the Step 3 dialog box, select the None item (default) to exclude compound
document support from the program, make sure that the Automation option
isn’t checked to eliminate automation support, and remove the check from the
ActiveX Controls option since you won’t be adding any ActiveX controls to the
program. Click the Next > button to display the Step 4 dialog box.
II. In the Step 4 dialog box, remove the check from each of the application features
except “3D Controls” (default) and leave the value 4 (default) as the number of
files you want to use in the “recent file list-** You don’t need to click the
Advanced... button to select advanced options; rather, you'll accept the default
values for these options.
The File menu of the generated program will list the most recently opened
documents; the number that you specify for the “recent file list” is the maximum
number of documents that will be listed. Click the Next > button to display the
Step 5 dialog box.
12. In the Step 5 dialog box, select the MFC Standard project-style option (default)
to generate the traditional MFC user interface for your program (the Windows
Explorer option implements the application as a workbook-like container). Select
the “Yes, Please” option (default) to have AppWizard include comments within
the source files it generates. The comments help clarify the code and clearly
indicate the places where you need to insert your own code. And finally, choose
the As Statically Linked Library option for the MFC library that’s used. With
the As A Statically Linked Library option, the MFC code is bound directly into
your program’s executable file. With the alternative option, As A Shared DLL,
your program accesses MFC code contained in a separate dynamic link library
(DLL), which can be shared by several applications (note that you’ll have to
select this option if you have the Standard Edition of Visual C++, which doesn’t
provide static MFC linking). The DLL option reduces the size of your program’s
executable file but requires you to distribute a separate DLL file together with
your program file (as you must when you distribute a Visual Basic program).
Click the Next > button to display the Step 6 dialog box.
13. The Step 6 dialog box displays information on each of the four main classes that
AppWizard defines for your program.. Don’t change any of this information
because the remainder of the exercise assumes that you’ve accepted all the default
values. This is the final AppWizard dialog box for collecting information; you
should now click the Finish button to display the New Project Information
dialog box.
14. The New Project Information dialog box, summarizes many of the program
features that you chose in the previous dialog boxes. (If you want to change any
feature, you can click the Cancel button and then go back to the appropriate
dialog box to adjust the information.) Click the OK button in the New Project
Information dialog box, and AppWizard will create the project folder that you
specified (if necessary), generate the program source files, and open the newly
created project, WinGreet.
Zero to Mastery Microsoft Visual in C++
protected:
char *m_Message;
public:
char ^GetMessage ()
return m_Message;
}
protected: // create from serialization only
CWinGreetDoc();
DECLARE_DYNCREATE(CWinGreetDoc)
// remainder of CWinGreetDoc definition ...
Generating a Windows
The code excerpt above shows the beginning of the CWinGreelDoc class
definition, and includes the code that was generated by AppWizard, as well as
the lines of code that you manually add, which are marked in bold. In the
instructions given in this part of the book, all lines о I code that you manually
add or modify are marked in bold. Although you add or modify only the bold
lines, the book typically shows a larger block of code to help you find the
correct position within the generated source file to make your additions or
modifications.
3. Open the file WinGreetDoc.cpp, which is the implementation file for the program’s
document class, CWinGreelDoc. Within the CWinGreetDoc class constructor,
add the statement that’s marked in bold in the following code:
// CWinGreelDoc construction/destruction
CWinGreelDoc: :CWinGreetDoc()
As a result of adding this line, the data member m_Message will automatically
be assigned the address of the string "Greetings!” when an instance of the
CWinGreetDoc class is created.
4. Open the file Win Greet View.cpp, which is the implementation file for the
program’s vtew class; this class is named CWinGreetView and is derived from
the MFC class CView. As you’ll see later, the view class is responsible for
processing input from the user and for managing the view window, which is
used for displaying the program data.
5. In the file WinGreetView.cpp, add the statements marked in bold to the
CWinGreetView member function OnDraw:
////////////
// CWinGreetView drawing
void CWinGreetView::OnDraw(CDC* pDC)
RECT ClientRect;
GetClientRect (&ClientRect);
pDC->DrawText
(pDoc->GctMessage (), // obtain the string
-I,
&ClientRect,
DTCENTER I DT_VCENTER I DT.SINGLELINE);
The MFC calls the OnDraw member function of the program’s view class whenever
the program window needs drawing or redrawing (for example, when the window is
first created, when its size is changed, or when it’s uncovered after being hidden by
another window). The code you added to OnDraw displays the siring that’s stored in
the docu me nt class (“Greetings I”), OnDraw obtai n s a poi n te г to the program ’ s document
class by calling the CView member function GelDocumenL It then uses this pointer to
call the CWinGreetDoc member function GetMessage (which you added to the code
in step 2) to obtain the message string. Although this is an elaborate method for
getting a simple string, it’s used here because it illustrates the typical way that the
view class obtains program data from the document class, so that it can display this
data. OnDraw is passed a pointer to a device context object that is an instance of the
MFC class CDC* A device context object is associated with a specific device (in
WinGreet it’s associated with the view window), and it provides a set of member
functions for displaying output on that device. OnDraw uses the CDC member function
DrawText to display the message string. To center the string within the view window,
it calls the CWnd member function GetClientRect to obtain the current dimensions of
the view window, and then supplies these dimensions (in a RECT structure) to
DrawText, together with a set of flags that cause DrawText to center the string
horizontally and vertically within the specified dimensions (DT_CENTER and
DT_VCENTER). In a full-featured application, you would of course make many
more changes to the source code generated by AppWizard, typically using a variety of
tools, including the resource editors and ClassWizard.
The WinGreet program is known as a single document interface (or SDI) application,
meaning that it displays only one document at a time. When AppWizard generates an
SDI application, it derives four main classes:
• The document class.
• The view class.
• The main frame window class.
• The application class.
The primary program tasks are divided among these four main classes, and AppWizard
creates separate source files for each class. By default, it derives the names of both the
classes and the class source files from the name of the project (though, as mentioned
previously, you can specify alternative names when using AppWizard to generate the
program). The WinGreet document class is named CWinGrectDoc and is derived
from the MFC class CDocument. The CWinGreetDoc header file is named
WinGreetDoc.h and the implementation file is named WinGreetDoc.cpp.
Zero to Mastery Microsoft Visual in C++
The document class is responsible for storing the program data as well as for reading
and writing this data to disk files. The WinGreet document class stores only a single
message string and doesn’t perform disk I/O.
The WinGreet view class is named CWinGreetView and is derived from the MFC
class CView. The CWinGreetView header file is named WinGreetView.h, and the
implementation file is named Win Greet Vie w.cpp. The view class is responsible for
displaying the program data (on the screen, printer, or other device) and for processing
input from the user. This class manages the view window, which is used for displaying
program data on the screen. The WinGreet view class merely displays the message
string within The WinGreet main frame window class is named CMainFrame and is
derived from the MFC class CFrameWnd.
The CMainFrame header file is named MainFrm.h, and the implementation file is
named MainFrm.cpp. The main frame window class manages the main program
window, which is a frame window that contains a window frame, a title bar, a menu
bar, and a system menu. The frame window also contains Minimize, Maximize, and
Close boxes, and sometimes other user interface elements such as a toolbar or a status
bar.
Note that the view window—managed by the view class—occupies the empty portion
of the main frame window inside these interface elements (which is known as the
client area of the main frame window). The view window has no visible elements
except the text and graphics that the view class explicitly displays (such as the string
"Greetings!” displayed by WinGreet). The view window is a child of the main frame
window, which means—among other things—that it’s always displayed on top of and
within the boundaries of the client area of the main frame window.
Finally, the application class is named CWinGreetApp and is derived from the MFC
class CWinApp. The CWinGreetApp header file is named WinGreet.h, and the
implementation file is named WinGreet.cpp. The application class manages the program
as a whole; that is, it performs general tasks that don’t fall within the province of any
of the other three classes, such as initializing the program and performing the final
program cleanup. Every MFC Windows program must create exactly one instance of
a class derived from CWinApp.
The four main classes communicate with each other and exchange data by calling
each other’s public member functions and by sending messages
AppWizard and the Developer Studio create several source and settings files in addition
to the source files for the four main classes
The following listings provide the complete text of the header and implementation
files for the four main program classes. These listings contain the code that was
generated by AppWizard, plus the manual code additions.
Listing 1
// WinGreet.h : main header file for the WINGREET application
//
#if
!defined(AFX_WlNGREET_H_E7D60DA4_9891_ I1D l_80rc_00C0F6A83B7P_INCLODED_J
^define
AFX_W1NGREET_H_E7D60DA4.9891J IDI _8OFC_OOCOF6A83B7F_1NCLCDED_
#if_MSC_VER> 1000
tfpragma once
#endif// _MSC_VER > 1000
#i fndef _AFX WIN_H_
terror include ‘stdafx.1T before including this file for PCH
#endif
^include “resource*!?’ // main symbols
// CWinGreetApp:
// See WinGreet.cpp for the implementation of this class
Title
//
class CWinGreetApp : public CWinApp
public:
CWinGreetAppO;
// Overrides
// Class Wizard generated virtual function overrides
//{{AFX_VlRTUAL(CWinGreetApp)
public:
virtual BOOL InitlnstanceQ;
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CWinGreetApp)
afx_msg void OnAppAboutQ;
// NOTE - the ClassWizard will add and remove member functions here*
// DO NOT EDIT what you see in these blocks of generated code ’
Zero to Mastery Microsoft Visual in C++
//} }AFX_MSG
DECLARE_MESSAGE_MAP()
};
1111ШШ11ШШ1Ш11111Ш11ШШШ1Ш1Ш111Ш11111ШШ11111111Ш
//{{AFX_1NSERT_LOCAT1ON) 1
// Microsoft Visual C++ win insert additional declarations immediately before
H the previous line.
#endif
/ /
!defined(AFX_WlNGREEr_H_E7D60DA4_989]_ 11D l_8()FC_OOCOF6A83B7F_INCLUDEDj
Listing 2
// WinGreet.cpp : Defines the class behaviors for the application.
и
Sinclude “stdafir.h”
#include “WinGreet.h”
#include “MainFrm.h”
ttinclude “WinGreetDoc.h"
^include “WinGreelView.h”
#ifdef .DEBUG
#define new DEBUG.NEW
#undef THIS.FILE
static char TH1S.FILEU = _FILE_;
#endif
iimiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
// CWinGreetApp
BEGIN_MESSAGE_MAP(CWinGreetApp, CWinApp)
//{{AFX_MSG_MAP(CWinGreetApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// NOTE - the Class Wizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//JJAFX.MSG.MAP
// Standard file based document commands
ON_COMMAND(ID_F1LE_NEW, CWinApp ::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp: :OnFileOpen)
Generating a Windows
END_MESSAGE_MAP()
// CWinGreetApp construction
CWinGreetApp::CWinGreetApp()
I
// TODO: add construction code here,
// Place all significant initialization in Ini tin stance
}
// CWinGreetApp initialization
BOOL CWinGreetApp::lnitlnstance()
// Standard initialization
// if you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
ttifdef _AFXDLL
EnableJdControlsQ; // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
tfendif
// Change the registry key under which our settings are stored.
// You should modify this string to be something appropriate
// such as the name of your company or organization.
SetRegis try Key (_Tf‘Local AppWizard-Generated Applications”));
LoadStdProfileSettingsO; // Load standard INI file options (including MRU)
// Register the application’s document templates. Document templates
// serve as the connection between documents, frame windows and views.
CSinglcDocTcmplate* pDocTem plate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAIN FRAME,
RUNTIME_CLASS(CWinGreetDoc),
Zero to Mastery Microsoft Visual in C++
public:
CAboufDlgO;
// Dialog Data
//| |AFX_DATA(CAboutDlg)
enum { IDD = IDD.ABOUTBOX };
//}}AFX_DATA
//ClassWizard generated virtual function overrides
H{{AFX_VlRTUAL(CAboutDlg)
protected:
virtual void DoDataExchangefCDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
// No message handlers
//} }AFX_MSG
DECL ARE_MESSAGE_M APO
Generating a Windows
9
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::lDD)
//{{AFX_DATA_IN1T(C AboutDlg)
//}} AFX_DATA_INIT
BEGlN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(C AboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// App command to run the dialog
void CWinGreetApp::OnAppAbout()
CAboutDIg aboutDlg;
aboutDlg.DoModal();
1Ш1ШШНШ1ШШ111Ш111Ш1111ШШ11111111111111ШШШ1ШШ11
H CWinGreetApp commands
Listing 3
// WinGreetDoc.h : interface of (he CWinGreetDoc class
//
Ш11ШШ1ШШ1Ш111ШШШШШ11ШШ1111Ш1Ш1111Ш11111111111
#if
!defired(AFX_WlNGRfcEIDOCJl_E7D6№AA_9l®]_l ID! JCFC_(XX3OF6/^!3B7F_1NQJJDEDJ
#del'ine
AFX_WINGREbTLXX_H_E7D60DAA_9891_ 11D1 _80FCjm)F6A83B7F_INCLUDED_
#if_MSC_VER> 1000
Zero to Mastery Microsoft Visual in C++
tfpragma once
#endif//_MSC_VER> 1000
Title
protected:
char *m_Message;
public:
char *GetMessage ()
return m_Message;
llllllliillilllllllllllllllllllllllllllllllllilllllllllllllllllllllllll
//{{AFX_1NSERT_LOCAT1ON) |
// Microsoft Visual C++ will insert additional declarations immediately before
H the previous line.
#endif
//
'±liird(AbX_WlNCiRlimXXLH_E7LM»AA_%M|_l ID I_8QKJXOHA83B7F_lNCLUDtDj
Listing 4
// WinGreetDoc.cpp : implementation of the CWinGreetDoc class
//
#include “stdafx.h”
#include “WinGreet.h”
tfinclude “WinGreetDoc.b”
#ifdef .DEBUG
#define new DEBUG.NEW
tfundef THIS.FILE
static char TH1S.FILEU = _F1LE_;
#endif
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH
H CWinGreetDoc
lMPLEMENT_DYNCREATE(CWinGreetDoc, CDocument)
BEGlN_MESSAGE_MAP(CWinGreetDoc, CDocument)
//{{AFX_MSG_MAP(CWinGreetDoc)
II NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//JJAFX.MSG.MAP
Zero to Mastery Microsoft Visual in C++
122
END_MESSAGE_MAP()
iillllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
IICWinGreelDoc construed on/destructi on
CWinGreetDoc: :CWinGreetDoc()
{
// TODO: add one-time construction code here
m_Message = “Greetings!”;
}
CWinGreetDoc:: 42 WinGreetDocQ
{
}
BOOL CWinGreetDoc: :OnNewDocument()
{
if (!CDocument::OnNewDocunientO)
return FALSE;
// TODO: add reinitialization code here
// (SD1 documents will reuse this document)
return TRUE;
}
llllllllllllllltllllllllllllllllllllllllllllllinilllllllllllllllllllllllllll
II CWinGreetDoc serialization
void CWinGreetDoc::Serialize(CArchive& ar)
{
if (ar.IsStoringO)
1
// TODO: add storing code here
}
else
{
H TODO: add loading code here
}
}
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
//CWinGreetDoc diagnostics
#ifdef .DEBUG
Generating a Windows GUI Program
//CWinGreetDoc commands
Listing 5
// MainFrm.h : interface of the CMainFrame class
#if
!delined(AFX_MAlNFRM_H_E7D60DBB_9891_l ID 1_80FC_OOCOF6A83B7F_1NCLUDED_J
^define
AFX_MAINFRM_H_E7D60DBB_9891.11D1 _80FC_OOCOF6A83B7F_INCLUDED.
tfif.MSC.VER > 1000
tfpragma once
#endif//.MSC.VER > 1000
class CMainFrame : public CFrameWnd
{
protected: // create from serialization only
CMainFrameO;
DECLARE.DYNCREATE(CMainFrame)
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX.VIRTUAL(CMainFrame)
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//JJAFXVIRTUAL
Zero to Mastery Microsoft Visual in C++
H Implementation
public:
virtual -CMamFrameQ;
ttifdef -DEBUG
virtual void AssertValidf) const;
virtual void Dump(CDumpContext& de) const;
tfendif
// Generated message map functions
protected:
//{{AFX_MSG(CMainFrame)
// NOTE - the ClassWizard will add and remove member functions here*
// DO NOT EDIT what you see in these blocks of generated code’
//} }AFX_MSG
DECL ARE_MESSAGE_MAP()
};
///////////////////////////^
H{ {AFX_1NSERT_LOCAT1ON}}
// Microsoft Visual C++ will insert additional declarations immediately before
// the previous line*
tfendif
//!defined(AFX_MA!NFRM_H_E7D60DBB_9891_1ID l_
80FC_OOCOF6A83B7F__1NCLUDED_)
Listing 6
// MainFrm*cpp : implementation of the CMainFrame class
П
^include “stdafich”
^include “WinGreetlT
#include “MainFrm.h"
ttifdef -DEBUG
^define new DEBUG_NEW
#undef THIS_F1LE
static char TH1S_F1LE[J = _F1LE_;
tfendif
1Ш1111П1НШШ1ШШШ1Ш1111111111Ш1111111111Ш1ШШШНШШ
Generating a Windows GUI Program
// CMainFrame
lMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGlN_MESSAGE_MAP(CMainFTame, CFrameWnd)
//{{AFX_MSG_MAP(CMa inFrame)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code ’
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CMainFrame construction/destruction
CMa in Frame ::CMainFrame()
CMainFrame::-CMainFrameO
if( !CFrameWnd::PreCreateWindow(cs))
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return TRUE;
// CMainFrame diagnostics
ttifdef .DEBUG
void CMainFrame::AssertValidO const
CFrameWnd::AssertValid();
CFrameWnd: :Dump(dc);
иорщиэшарЗш] //
1УП1И1Л"ХЯУ{{//
:pai33iojd
■С53 ЮОЯ 1*тлл
мэ1л slip мшр 01 иэрршэло // tf^gd $DC1D)MCIGLJO Р!ол |впцтл
:ojiqnd
(M0iAi90JOuiM3)lVniHIA-XJV}}//
зэрщало uopounj ргпцтл раделэизЗ р-гехтд^ящз //
SOpUJOAQ 11
:ojiqnd
suoijEiado //
!()шэи1пэо<раг) ^ooamiQiiiMD
:ojiqnd
sajnqunv //
(м91л^э10и1Мэ)ы1Уаяэыла_ааучэяа
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before
// the previous line.
#endif
//kfefined(AFX_WINGREEIVIEW_H_E7D6ODAC_989]_l lDI_^rc_(RIlW3B7F_^CLUDEDJ
Listing 8
// WinGreetViewxpp : implementation of the CWinGreetView class
//
#include “stdafx.h”
^include inGreet. If’
^include <cWinGreetDoc.h”
#include “Wi n GreetVi ew.h”
in C++
flifdef _DEBUG
fldefine new DEBUG_NEW
flu n de I THIS_FILE
static char TH1S_FILEU = _F1LE_;
flendif
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
// CWinGreetView
!MPLEMENT_DYNCREATE(CWinGreetView, CView)
BEGIN_MESSAGE_MAP(CWinGreetView, CView)
{AFX_MSG_MAP(CWinGreetView)
// NOTE - the Class Wizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Ш11ШШ1ШШ1Ш111ШШШШШ11ШШ1111Ш1Ш1111Ш11111111111
// CWinGreetView const ruction/destruction
C WinGreet V ie w: :C Wi nGreetVie w()
CWinGreetView: :~CWinGreetView()
iillllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllHIIIIIIIIIII
H CWinGreetView drawing
void CWinGreetView: :OnDraw(CDC* pDC)
// CWinGreetView diagnostics
tfifdef-DEBUG
void CWinGreetView: :AssertValid() const
CView::AssertValid();
CView::Dump(dc);
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CWinGreetDoc)));
return (CWinGreetDoc*)m_pDocument;
#endif//-DEBUG
The following is a list of some of the significant events that occur, when you run the
WinGreet program. These five events were selected from the many program actions
that take place, because they best help you understand how the WinGreet program
works and they illustrate the purpose of the different parts of the source code:
1. The CWinApp class constructor is called.
2. The program entry function, WinMain, receives control
3. WinMain calls the program’s Initlnstance function.
4. WinMain enters a loop for processing messages.
5. WinMain exits and the program terminates.
CWinGreetApp construction
CWinGreetApp: :CWinGreetApp()
Such a do-nothing constructor causes the compiler to invoke the default constructor
of the base class, which is CWinApp. The CWinApp constructor (supplied by the
MFC) performs the following two important tasks:
• It makes sure that the program declares only one application object (that is, only
one object belonging to CWinApp or to a class derived from it).
• It saves the address of the program’s CWinGreetApp object in a global pointer
declared by the MFC. It saves this address so that the MFC code can later call
the WinGrectApp member functions. Calling these member functions will be
described under step 3.
Generating a Windows GUI Program
131
2, WinMain Receives Control: After all global objects have been created, the program
entry function, WinMain, receives control. This function is defined within the MFC
code; it’s linked to the WinGreet program when the executable file is built. The
WinMain function performs many tasks. The following steps describe the tasks that
are the most important for understanding how the WinGreet program works,
3. WinMain Calls Initlnstance: Shortly after it receives control, WinMain calls the
Initlnstance member function of the CWinGreetApp class. It calls this function by
using the object address that the CWinApp constructor saved in step I. Initlnstance
serves to initialize the application,
4, WinMain Processes Messages: After completing its initialization tasks, WinMain
enters a loop that calls the Windows system to obtain and dispatch all messages sent to
objects within the WinGreet program (this loop is actually contained in a CWinApp
member function named Run that’s called from WinMain), Control remains within
this loop during the remaining time that the application runs. Under Windows 95
(and later) and Windows NT, however, preemptive multitasking allows other programs
to run at the same time.
5. WinMain Exits and the Program Terminates: When the user of the WinGreet
program chooses the Exit command on the File menu or the Close command on the
system menu, or clicks the Close box, the MFC code destroys the program window
and calls the Win32 API function: PostQuitMessage, which causes the message loop
to exit. The WinMain function subsequently returns, causing the application to
terminate.
// Change the Registry key under which our settings are stored.
//You should modify this string to be something appropriate
// such as the name of your company or organization.
SetRegistryKey(_T(“Local AppWizard-Generated Applications”));
To customize the name of the key under which the program settings are stored (for
example, to set it to your company name), simply replace the string passed to
SetRegistryKey. (Note that the macro _T converts the string to Unicode format, which
SetRegistryKey requires. This format stores each character as a 16-bit value, and can
be used to encode the characters in any language.)
The primary setting stored in the Registry is the list of most recently opened documents
that’s displayed on the program’s File menu (which is also known as the MRU, or Most
Recently Used, file list). Initlnstance loads this document list, as well as any other program
settings stored in the Registry, by calling the CWinApp::LoadStdProfileSettings function:
LoadStdProfileSettingsQ; // Load standard INI file options
// (including MRU)
If you need to perform any other application initialization tasks, the Initlnstance
function is the place to add the code.
REVIEW EXERCISE
WINDOWS, DIALOG
BOXES AND CONTROLS
created by Windows at startup time. The parent window for top-level windows is the
desktop window; the parent window for child windows is either a top-level window or
another child window higher up in the hierarchy. Figure 7.1 demonstrates this hierarchy
by dissecting a typical Windows screen.
Actually, the situation under Windows NT is somewhat more complex. Unlike its
simpler cousins, Windows NT has the capability to maintain multiple desktops
simultaneously. In fact, Windows NT normally maintains three desktops: one for the
Winlogon screen, one for user applications, and one for the screen saver.
The visual window hierarchy normally reflects the logical hierarchy. That is, windows
at the same hierarchy level are normally displayed in the Z-order, which is essentially
the order in which siblings appear. However, this order can be changed for top-level
windows. Top-level windows with the extended window style WM_EX_TOPMOST
appear on top of any non-topmost top-level windows.
Another relationship exists between top-level windows. A top-level window may
have an owner. which is another top-level window. An owned window always appears
on top of its owner and disappears if its owner is minimized. A typical case of a top
level window owned by another occurs when an application displays a dialog box.
The dialog box is not a child window (it is not confined to the client area of the
application’s main window), but it remains owned by the application window.
Several functions enable applications to traverse the window hierarchy and find a
specific window. Here’s a review of a few of the more frequently used functions:
Windows Dialog Boxes and Controls
137
GetDesktop Window. Through the GetDesktopWindow function, an application can
retrieve the handle of the current desktop window.
EnumWindows. The EnumWindows function enumerates all top-level windows. A
user-defined callback function, the address of which is supplied in the call to
EnumWindows, is called once for every top-level window. EnumWindows does not
enumerate top-level windows that are created after the function has been called, even
if it has not yet completed the enumeration when the new window is created.
EnumChildWindows. The EnumChildWindows function enumerates all child windows
of a given window, identified by a handle that is supplied in the call to
EnumChildWindows. The enumeration is accomplished by a user-defined callback
function, the address of which is also supplied in the call to EnumChildWindows.
This function also enumerates descendant windows; that is, child windows that are
themselves children (or descendants) of child windows of the window specified in the
call to EnumChildWindows.
Child windows that are destroyed before they arc enumerated, or child windows that
are created after the enumeration process started, will not be enumerated.
EnumThreadWindows. The EnumThreadWindows function enumerates all windows
owned by a specific thread by calling a user-supplied callback function once for every
such window. The handle to the thread and the address of the callback function are
supplied by the application in the call to EnumThreadWindows. The enumeration
includes top-level windows, child windows, and descendants of child windows.
Windows that are created after the enumeration process began are not enumerated by
EnumThreadWindows.
FindWindow. The Find Window function can be used to find a top-level window by
its window class name or window title.
GetParent. The GetParent function identifies the parent window of the specified
window.
GelWindow. The GetWindow function offers the most flexible way for manipulating
the window hierarchy. Depending on its second parameter, uCmd, this function can
be used to retrieve the handle to a window’s parent, owner, sibling, or child windows.
Typically, an application creates a window in two steps. First, the window class is
registered; next, the window itself is created through the CreateWindow function.
The window class determines the overall behavior of the new window type, including
most notably the address of the new window procedure. Through CreateWindow the
application controls minor aspects of the new window, such as its size, position, and
appearance.
Zero to Mastery Microsoft Visual in C++
DefWindowProc* The messages can be anything: window sizing and moving, mouse
events, keyboard events, commands, repaint requests, timer and other hardware-related
events, and so on.
A typical window procedure contains a large switch statement block* Inside, case
blocks exist for every message the application is interested in. Messages that the
application does not handle are passed to DefWindowProc through the default block*
The skeleton of such a window procedure is shown in Listing 9,1 *
switch(uMsg)
case WM.DESTROY:
PostQuitMessage(O);
break;
// Other case blocks come here
default:
return DefWindowProcfhwnd, uMsg, wParam, IParam);
return 0;
Certain global characteristics of the window class are controlled through the class
style parameter, style* This parameter may be set to a combination of values (using
the bitwise OR operator, I). For example, CS_BYTEAL1GNCLIENT specifies that
the window’s client area is always to be positioned on a byte boundary in the screen
display’s bitmap to enhance graphics performance (a very useful thing to remember
when writing performance-intensive applications intended to run on lower-end graphics
hardware). The value CS_DBLCLKS specifies that Windows should generate double
click mouse messages when the user double-clicks the mouse within the window* The
pair of values CS_HREDRAW and CS_VREDRAW specify that the window be
redrawn in its entirety every time its horizontal or vertical size changes* Or the value
CS_SAVEB1TS specifies that Windows should allocate what UNIX and X programmers
often refer to as backing store; a copy of the window bitmap in memory, so that it can
Zero to Mastery Microsoft Visual in C++
automatically redraw the window when parts of it become unobscured. (This should
be used with caution; the large amounts of memory required for this may cause a
significant performance hit.)
Note: In 16-bit Windows, it was possible to register an application global class through (he style
CS-GLOBALCLASS, An application global class was accessible from all other applications
and DLLs. This is not true in Win32, In order for an application global class to work as intended,
it must be registered from a DLL that is loaded by every application. Such a DLL can be defined
through the Registry,
);
The first parameter, IpClassName, defines the name of the class that this window
inherits its behavior form. The class must either be registered ihrough RegisterClass
or be one of the predefined control classes. The predefined classes include the
BUTTON, COMBOBOX, EDIT, \, SCROLLBAR, and STATIC classes. There are
also some window classes that are mostly used internally by Windows and are referenced
only through integer identifiers; these include classes for menus, the desktop window,
and icon titles, to name but a few.
The dwStyle parameter specifies the window’s style. This parameter should not be
confused with the class style, passed to RegisterClass through the WNDCLASS structure
when the new window class is registered. While the class style determines some of the
Windows Dialog Boxes and Controls
141
permanent properties of windows belonging to lhat class, the window style passed to
CreateWindow is used to initialize the more transient properties of the window. For
example, dwStyle can be used to determine the window’s initial appearance (minimized,
maximized, visible or hidden). As is the case with the class style, the window style is
also typically a combination of values (combined with the bitwise OR operator). In
addition to the generic style values that are common to all types of windows, some
values are specific to the predefined window classes; for example, the
BS_PUSHBUTTON style can be used for windows of the BUTTON class that are to
send WM_COMMAND messages to their parents when clicked.
Some dwStyle values are important enough to deserve a closer look.
The WS_POPUP and WS_OVERLAPPED styles specify top-level windows. The
basic difference is that a WS_OVERLAPPED window always has a caption, while a
WS_POPUP window does not need to have one. Overlapped windows are typically
used as the main window of applications, while popup windows are used for dialog
boxes.
When a top-level window is created, the calling application sets its owner window
through the hwndParent parameter. The parent window of a top-level window is the
desktop window.
Child windows are created with the WS_CH1LD style. The major difference between
a child window and a top-level window is that a child window is confined to the client
area of its parent.
Windows defines some combinations of styles that are most useful when creating
“typical” windows. The WS_O VERLAPPEDWINDOW style setting combines the
WS.OVERLAPPED style with the WS_CAPTION, WS.SYSMENU,
WS.THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles to
create a typical top-level application window. The WS_POPUPWINDOW style setting
combines WS.POPUP with the WS_BORDER and WS.SYSMENU styles to create
a typical dialog box.
Other extended window styles are specific to Windows 95 and versions of Windows
NT later than 3.51; for example, Windows NT 3.51 with the beta version of the
Windows 95 style shell installed. For example, the WS_EX_TOOLWINDOW style
can be used to create a tool window. A tool window is a window with a smaller than
usual title bar and other properties that make it useful as a floating toolbar window.
Yet another set of Windows 95 specific extended styles specifies (he window’s behavior
with respect to the selected shell language. For example, the WS_EX_R1GHT,
WS_EX_RTLREAD1NG, and WS_EX_LEFTSCROLLBAR extended styles can be
used in conjunction with a right-to-left shell language selection such as Hebrew or
Arabic.
Painting in a window is performed through the normal set of GDI drawing functions.
Applications usually obtain a handle to the display device context through a function
such as GetDC, and then call GDI functions such as LineTo, Rectangle, or TextOut.
But even more typically, window painting occurs in response to a specific message,
WM.PAINT.
Note: The BeginPaint function should only be called in response to a WM_PAINT message. I
Each call to BeginPaint must be accompanied by a subsequent call to the EndPaim function, I
Applications can use the hDC member of the structure to draw into the client area of
the window. The rePaint member represents the smallest rectangle that encloses all
areas of the window that require updating. By limiting their activities to this rectangular
region, applications can speed up the painting process.
a confirmation dialog and call Destroy Window only if the user confirms closing the
window.
WM_QUIT. The WM_QU1T message is usually the last message an application’s
main window receives. Receiving this message causes GetMessage to return zero,
which terminates the message loop of most applications.
This message indicates a request to terminate the application, it is generated in response
to a call to PostQui [Message.
WM.QUERYENDSESSION. The WM.QUERYENDSESSION notifies the
application that the Windows session is about to be ended. An application may return
FALSE in response to this message to prevent the shutdown of Windows. After
processing the WM_QUERYENDSESSION message, Windows sends all applications
a WM.ENDSESS10N message with the results of the WM.QUERYENDSESSION
processing.
WM_ENDSESS10N. The WM_ENDSESSION message is sent to applications after
the WM_QUERYENDSESS10N message has been processed. It indicates whether
Windows is about to shut down or whether the shutdown has been aborted.
If an imminent shutdown is indicated, the Windows session may end at any time after
the WM_ENDSESS10N message has been processed by all applications. It is important,
therefore, that applications perform all tasks pertaining to sale termination.
WM_ACTIVATE. The WM_ACT1VATE message indicates when a top-level window
is about to be activated or deactivated. The message is first sent to the window that is
about to be deactivated, then to the window that is about to be activated.
WM_SHOWW1NDOW. The WM_SHOWWINDOW message indicates when a
window is about to be hidden or shown. A window can be hidden as a result of a call
to the ShowWindow function, or as a result of another window being maximized.
WM_ENABLE. The WM_ENABLE message is sent to a window when it is enabled
or disabled. A window can be enabled or disabled through a call to the EnableWindow
function. A window that is disabled cannot receive mouse or keyboard input.
WM_MOVE. The WM_MOVE message indicates that the window’s position has
been changed.
WM_S1ZE. The WM_S1ZE message indicates that the window’s size has been changed.
WM_SETFOCUS. The WM_SETFOCUS message indicates that the window has
gained keyboard focus. An application may, for example, display the caret in response
to this message.
WM_K1LLFOCUS. The WM.KILLFOCUS message indicates that the window is
about to lose keyboard focus. If the application displays a caret, the caret should be
destroyed in response to this message.
Windows Dialog Boxes and Controls
145
WM_GETTEXT. The WM_GETTEXT message is sent to a window requesting that
the window text be copied to a buffer. For most windows, the window text is the
window title. For controls like buttons, edit controls, static controls, or combo boxes,
the window text is the text displayed in the control. This message is usually handled
by the DefWindowProc function.
WM_SETTEXT. The WM_SETTEXT message requests that the window text be set
to the contents of a buffer. The DefWindowProc function sets the window text and
displays it in response to this message.
Several messages concern the nonclient area of a window; that is, its title bar, border,
menu, and other areas that are typically not updated by the application program. An
application can intercept these messages to create a window frame with a customized
appearance or behavior.
WM_NCPA1NT. The WM_NCPA1NT message indicates that the nonclient area of a
window (the window frame) needs to be repainted. The DefWindowProc function
handles this message by repainting the window frame.
WM_NCCREATE. Before the WM_CREATE message is sent to a window, it also
receives a WM_NCCREATE message. Applications may intercept this message to
perform initializations specific to the nonclient area of the window.
WM.NCDESTROY. The WM.NCDESTROY message indicates that a window’s
nonclient area is about to be destroyed. This message is sent to a window after the
WM.DESTROY message.
WM_NCACTIVATE. The WM.NCACT1VATE message is sent to a window to indicate
that its nonclient area has been activated or deactivated. The DefWindowProc function
changes the color of the window title bar to indicate an active or inactive state in
response to this message.
Every window is associated with a window class. A window class is either a class
provided by Windows, or a user-defined window class registered through the
RegisterClass function.
sent or posted to that window. In doing so, it can rely on the default window procedure
(DefWindowProc, or in the case of dialog boxes, DefDlgProc) for the processing of
unwanted messages.
It is through the window procedure that the behavior of a window is implemented. By
responding to various messages, the window procedure determines how the window
reacts to mouse and cursor events and how its appearance changes in reaction to those
events. For example, in the case of a button, the window procedure may respond to
WM_LBUTTONDOWN messages by repainting the window indicating that the button
is pressed. Or in the case of an edit control, the window procedure may respond to a
WM_SETFOCUS message by displaying the caret,
Windows supplies two default window procedures: DefWindowProc and DefDlgProc,
The DefWindowProc function implements the default behavior for typical top-level
windows. It processes nonclient area messages and manages the window frame. It also
implements some other aspects of top-level window behavior, such as responding to
keyboard events; for example, responding to the Alt key by highlighting the first item
in the window’s menu bar.
The DefDlgProc function is for the use of dialog boxes. In addition to the default top
level window behavior, it also manages the focus within a dialog box. It implements
the behavior of dialogs whereby the focus jumps from one dialog control to the next
when the user presses the Tab key.
In addition to the default window procedures, Windows also supplies a set of window
classes. These implement the behavior of dialog box controls, such as buttons, edit
fields, list and combo boxes, and static text fields. The name for these classes is system
global class, which is a leftover from the days of 16-hit Windows, In Win32 these
classes are no longer global. That is, a change that affects a system global class will
only affect windows of that class within the same application and have no effect on
windows in another application because Win32 applications run in separate address
spaces, and thus they are shielded from one another.
Whether it is a Windows-supplied class, or a class defined by the application, an
application can use an existing window class from which to derive a new class and
implement new or modified behavior. The mechanisms for accomplishing this arc
called subclassing and superclassing.
Warning: An application should not attempt to subclass or superclass a window that belongs (o
another process.
7.5.2 Subclassing
Subclassing means substituting the window procedure for a window class with another.
This is accomplished by calling the SelWindowLong or SelClassLong function.
Windows Dialog Boxes and Controls
{
switch(uMsg)
case WM.LBUTTONUP:
Destroy Windowfhw nd);
break;
case WM_DESTROY:
PostQuitMessage(O);
break;
default:
return CallWindowProc(01dWndProc,
hwnd, uMsg, wParam, IParam);
return 0;
Zero to Mastery Microsoft Visual in C++
I would like to call your attention to the mechanism used in lhe new window procedure,
WndProc, to reference the old window procedure for the default processing of messages.
The old procedure is called through the Win32 function CallWindowProc. In 16-bit
Windows, it was possible to call the address obtained by the call to SetWindowLong
directly; this was always the address of the old window procedure. In Win32, this is
not necessarily so; the value may instead be a handle to the window procedure.
In this example, I performed the subclassing through SetWindowLong, meaning that
it only affected the single button window for which SetWindowLong was called. If I
had called SelClassLong instead, I would have altered the behavior of all buttons
created subsequently, Consider the example program in Listing 7,3 (to compile this
program from the command line, type cl subclass,c user32,lib).
{
switch(uMsg)
Windows Dialog Boxes and Controls
149
case WM_LBUTT0ND0WN:
MessageBeep(OxFFFFFFFF);
default:
return CallWindowProc(01dWndProc,
hwnd, uMsg, wParam, IParam);
return 0;
HWND hwnd;
hwnd = CreateWindowCBUTTON”, “ ”,
0, 0, 0, 0, 0,
NULL, NULL, hlnstance, NULL);
OldWndProc =
(WNDPROC)SetClassLong(hwnd, GCL.WNDPROC, (LONG)WndProc);
Destroy Window(hwnd);
MessageBox(NULL, “Hello, World!”, “ ”, MB_0K);
This example creates a button control but never makes it visible; the sole purpose of
this control’s existence is so that through its handle, the class behavior can be modified.
Immediately after the call to SetClassLong, the button control is actually destroyed.
But the effects of SetClassLong linger on! The subsequently displayed message box
contains an OK button; and the behavior of this button (namely that when it is clicked
by the left mouse button, the PC speaker emits a short beep) reflects the new window
procedure. Similarly, if the program displayed other dialogs or message boxes, indeed
anything that had button controls in it, all the newly created buttons would exhibit the
modified behavior
such as dialog controls. (This is how the 3-D control library CTL3D.DLL was
implemented-) Subclassing the window class affected all newly created windows of
that class, regardless of the application that created them. Unfortunately, in Win32
this is no longer the case; only windows of the same application are affected by such
a change.
So how can developers influence the global behavior of certain types of windows?
The answer is, you have to use a DLL and ensure that it is loaded into every application’s
address space.
Under Windows NT, this can be accomplished easily by creating a setting in the
registry- The following registry value needs to be modified:
\H KE Y_LOC AL_M ACHINE\Sof t w are\Mi or о so f t\Windo w s
NT\CurrentVersion\Windows\APPINlT_DLLS
DLLs that arc listed under this registry key are loaded into the address space of every
newly created process. If you wish to add several DLLs, separate the pathnames by
spaces.
Listing 9.4 shows a DLL that subclasses the BUTTON class just like the example
shown in Listing 9.3. If you add the full pathname of this DLL to the above-mentioned
registry key, every time a button control is clicked, a short beep will be heard.
switch(uMsg)
case WM.LBUTTONDOWN:
MessageBeep(OxFFFFFFFF);
default:
return CallWindowProc(01dWndProc,
hwnd, uMsg, wParam, IParam);
return 0;
Windows Dialog Boxes and Controls
151
HWND hwnd;
switch(dwReason)
case DLL_PROCESS_ATTACH:
hwnd = CreateWindow(“BUTTON”,
0, 0, 0, 0, 0,
NULL, NULL, hModule, NULL);
OldWndProc = (WNDPROC)SetClassLong(hwnd, GCL.WNDPROC,
(LONG)WndProc);
Destroy Window(hwnd);
return TRUE;
To compile this DLL from the command line, use cl ZLD beepbtn.c user32.1ib. The /
LD command line flag instructs the compiler to create a DLL instead of an executable
file.
Warning: Be careful to only add a fully tested DLL to the Registry. A faulty DLL may render
your system unstable or may prevent it from starting altogether. If that happens, a quick-and-
dirty remedy is to hoot into MS-DOS and rename the DLL file to prevent it from being loaded.
Obviously, if your DLL file sits on an NTFS partition, this may not be so easy to do.
Adding your DLL’s pathname to the APP1N1T_DLLS Registry key is perhaps the
simplest, but certainly not the only technique to inject your DLL’s code into another
application’s address space. Another drawback of this technique includes the fact that
a DLL specified this way is loaded into the address space of every application—or, to
be more precise, every GUI application that links with USER32.DLL. Even the slightest
bug in your DLL may seriously affect the stability of the entire system.
Fortunately, there are other techniques available that enable you to inject your DLL
into the address space of another process.
Zero to Mastery Microsoft Visual in C++
The first such technique requires the use of a Windows hook function. By using the
SetWindowsHookEx function, it is possible to install a hook function into the another
application's address space. Through this mechanism, you can add a new window
function to a window class owned by another application.
The second technique relies on the CreateRemoteThread function and its ability to
create a thread that runs in the context of another process.
7.5.4 Superclassing
Superclassing means creating a new class based on the behavior of an existing class.
An application that wishes to superclass an existing class can use the GetClasslnlb
function to obtain a WNDCLASS structure describing that class. After this structure
has been suitably modified, it can be used in a call to the RegisterClass function that
registers the new class for use.
The example shown in Listing 9.5 demonstrates the technique of superclassing. In
this example, a new window class, BEEPBUTTON, is created, its behavior based on
the default BUTTON class. This new class is then used to display a simple message.
To compile this program from the command line, type cl supercls.c user32.1ib.
switch(uMsg)
case WM.LBUTTONDOWN:
MessageBeep(OxFFFFFFFF);
default:
return CallWindowProc(OldWndProc,
hwnd, uMsg, wParam, IParam);
return 0;
Windows Dialog Boxes and Controls
153
int W1NAP1 WinMain(HlNSTANCE hlnstance, HINSTANCE d2,
LPSTR d3, int d4)
MSG msg;
HWND hwnd;
WNDCLASS wndClass;
GetClassInfo(hInstance, “BUTTON”, &wndClass);
wndClass. hlnstance = hlnstance;
wndClassdpszClassName = “BEEPBUTTON”;
OldWndProc = wndClassdpfnWndProc;
wndClass. IpfnWndProc = WndProc;
RegisterClass(&wndClass);
hwnd = CreateWindowCBEEPBUTTON”, “Hello, World!”,
WS_VISIBLE I BS_CENTER, 100, 100, 100, 80,
NULL, NULL, hlnstance, NULL);
while (GetMessage(&msg, NULL, 0, 0))
if (msg.message == WM_LBUTTONUP)
Destroy Window(hwnd);
PostQu itMessage(O);
DispatchMessage(&msg);
return msg.wParam;
We have looked at the difference between the two techniques, subclassing and
superclassing, in terms of their implementation. But what is the difference between
them in terms of their utility? In other words, when would you use subclassing, and
when would you use superclassing?
Zero to Mastery Microsoft Visual in C++
In addition to its main application window with its title and menu bar and application-
defined contents, an application most commonly uses dialogs to exchange information
with the user. Typically, the application’s main window exists throughout the life of
the application, while its dialogs are more transient in nature, popping up only for the
duration of a brief exchange of data; but this is not the key distinguishing characteristics
of a main window and a dialog. Indeed, there are applications that use a dialog box as
their main window; in other applications, a dialog may remain visible for most of the
application’s lifetime.
A dialog box usually contains a set of dialog controls, themselves child windows,
through which the user and the application exchange data. There are several Win32
functions that assist in constructing, displaying, and managing the contents of a dialog
box. Applications developers usually need not be concerned about painting a dialog’s
controls or handling user-interface events; instead, they can focus on the actual exchange
of data between the dialog’s controls and (he application.
Dialogs represent a versatile capability in Windows. To facilitate their efficient use,
Windows provides two types of dialog boxes: modeless and modal.
Note: System modal message boxes should be used very carefully. Few things are more annoying
than a misbehaving application that displays a system modal message box repeatedly in a loop
(perhaps due to a programming error), effectively rendering the entire system useless.
Zero to Mastery Microsoft Visual in C++
Win32 implements a series of commonly used dialogs, freeing the programmer from
the need to implement these for every application. These common dialogs are well
known to every Windows user. They include dialogs for opening and saving files,
selecting a color or a font, printing and setting up the printer, selecting a page size,
and searching and replacing text.
Common dialogs can be used in two ways. Applications can utilize the common
dialog “as is” by calling one of the common dialog functions that are part of the
Windows Dialog Boxes and Controls
157
Win32 API. Alternatively, applications can customize common dialogs by
implementing a special hook function and supplying a custom dialog template,
Windows 95 has introduced several changes to the common dialogs that were known
to Windows 3,1 and Windows NT programmers. However, most of these changes arc
cosmetic, and do not affect typical usage of the dialogs. Where the differences arc
significant.
Note: The appearance of all common dialog boxes has changed substantially in Windows 95,
Applications that supply their own dialog templates must lake this fact into account in order to
present a visual appearance that is consistent with the rest of the operating system.
Г Open is ised'Only
Forthose familiar with the Windows 3.1 look of the common Hie dialogs, the difference
between that and the new Windows 95 look is striking. Applications that wish to use
the new look (and take advantage of the new Explorer-related functionality) must
specify the style OFN_EXPLORER in the Flags member of the OPENFILENAME
structure.
The Windows 95 versions of the common file dialogs have another new feature.
When a file dialog is customized, it is no longer necessary to reproduce the entire
dialog template before adding your modifications. Instead, it is possible to create a
dialog template containing only the controls you wish to add to the dialog and an
optional special field, labeled with the ID stc32, indicating where the standard
components of the dialog should be placed.
the dialog’s behavior by supplying a hook function and the name of a custom dialog
template. When the dialog is dismissed, the new color selection is available as the
rgbResult member of the CHOOSE COLOR structure.
The font selection dialog is initialized through the CHOOSEFONT structure. This
structure can also be used to specify a custom hook function and the name of a custom
dialog template. The IpLogFont member of this structure points to a LOGFONT
structure that can be used to initialize the dialog and receives information about the
newly selected font when the dialog is dismissed. This structure can be used in a call
to the GDI function CreateFontlndirect to actually create the font for use.
Printer
Name: Properties
Where: LPT1:
Comment- Г Print lo file
₽ an Humbet cd fiopies: f 3
Г" £etectiui
G1 Cancel
The Replace dialog (Figure 7,9) is a close cousin to the Find dialog and is initialized
through an identical FINDREPLACE structure. This dialog is displayed in response
to a call to the ReplaceText function.
When the application receives a message from a Find or Replace dialog, it can check
the Flags member of the FINDREPLACE structure to determine what action was
requested by the user.
Note: The Find and Replace dialogs are not destroyed when the FindText or ReplaceText
functions return. For this reason, an application would normally allocate the FINDREPLACE
structure in global memory. If memory allocated for the FINDREPLACE structure is deallocated
before the Find or Replace dialogs are destroyed, the application will fail.
switch(uMsg)
case WM_DESTROY:
PostQuitMessage(O);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, IParam);
return 0;
}
ini WINAP1 WinMain(HINSTANCE hlnstance, H1NSTANCE hPrevInstance,
LPSTR d3, int nCmdShow)
Windows Dialog Boxes and Controls
MSG msg;
HWND hwnd;
WNDCLASS wndClass;
OPENFILENAME ofn;
CHOOSECOLOR cc;
CHOOSEFONT cf;
PRINTDLG pd;
PAGESETUPDLG psd;
FINDREPLACE fr;
COLORREF crCustColorsll6J;
LOGFONT If;
char szFindWhatl80J;
char szReplaceWith[80];
HWND hdlgFt, hdlgFr;
if (hPrevlnstance = NULL)
{
memset(&wndClass, 0, sizeof(wndClass));
wndClass.style = CS_HREDRAW I CS_VREDRAW;
wndClass .IpfnWndProc - WndProc;
wndClass.hlnstance = hlnstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + I);
wndClass.lpszClassName = “COMMDLGS”;
if (IRegisteiClassf&wndClass)) return FALSE;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
memset(&ofn, 0, sizeof(ofn));
ofhJStructSize = sizeof(OPENFILENAME);
GetOpenFileName(&ofn);
memset(&ofn, 0, sizeof(ofn));
ofhJStructSize = sizeof(OPENFILENAME);
GetSaveFileName(&ofn);
memset(&cc, 0, sizeof(cc));
memset(crCustCo]ors, 0, sizeof(crCustColors));
cc.lStructSize = sizeof(cc);
ccJpCustColors = crCustColors;
C hooseColor(£cc);
memset(&cf, 0, sizeoffcf));
memsetf&lf, 0, sizeofflf));
cfJStructSize = sizeof(cf);
cfJpLogFont = &lf;
cf.Flags = CF_SCREENFONTS I CF_EFFECTS;
ChooseFont(&cf);
memset(&pd, 0, sizeof(pd));
pdJStructSize = sizeof(pd);
PrintDlg(&pd);
memset(&psd, 0, sizeof(psd));
psd.lStructSize = sizeof(psd);
PageSetupDlg(&psd);
memset(&frf 0, sizeof(fr));
memset(szFindWhatT 0, sizeof(szFindWhat));
memsetfszReplaceWith, 0, sizeof(szReplaceWith));
frJStructSize = sizeof(fr);
Windows Dialog Boxes and Controls
165
fnhwndOwner = hwnd;
fr.lpstrFindWhat = szFindWhat;
fr.lpstrRepl ace With = szReplaceWith;
fr.wFindWhatLen = sizeof(szFindWhat);
fr.wReplaceWithLen = sizeof(szReplaceWith);
hdlgFt = FindText(&fr);
hdlgFr = ReplaceText(&fr);
while (GetMessage(&msg, NULL, 0, 0))
if(!IsDialogMessage(hdlgFt, &msg))
if(!IsDialogMessage(hdlgFr, &msg))
DispatchMessage(&msg);
return msg.wParam;
7.8 CONTROLS
A control is a special window that typically enables the user to perform a simple
function and sends messages to this effect to its owner window. For example, a
pushbutton control has one simple function, namely that the user can click on it; when
that happens, the pushbutton sends a WM_COMMAND message to the window
(typically a dialog) that owns it.
Windows offers several built-in control classes for the most commonly used controls.
A dialog with a sample collection of these controls is shown in Figure 7.10.
Windows 95 introduced a set of new control classes, collectively referred to as Windows
95 Common Controls. This name is slightly misleading as the new control classes arc
now also available in Windows NT 3.51 and Win32s 1.3.
Applications can also create their own controls. These can be derived from the standard
control classes, or they can be built independently.
Zero to Mastery Microsoft Visual in C++
The control class and the control style (which defines variations of behavior within a
button class) are usually both defined in an application’s resource file. Alternatively,
applications that create controls programmatically select the button class and specify
the button style as parameters to the Create Window function.
7.8.2 Buttons
Buttons, as their name implies, are controls that respond to simple mouse clicks.
There are several button types. A pushbutton is a button that posts a WM_COMMAND
message to its owner window when it is clicked. A check box indicates one of two
states, selected and not selected. A variant of the check box, the three-state check box,
adds a third, disabled state to the other two. A radio button is a control that is typically
used in groups, indicating a set of mutually exclusive choices.
There are variants to these control styles that define secondary aspects of their behavior.
example, the client area of the Windows Notepad application is one large edit control.
Applications typically communicate with the edit control through a series of messages
that are used to set or retrieve text from the control
7.8.6 Scrollbars
A scroll bar control consists of a rectangular area with two arrows at the end and a
sliding pointer. A scroll bar can be vertical or horizontal. Scrollbars arc typically used
to indicate the position of the visible portion within a larger area. Applications also
used scrollbars to implement the functionality of a slide control; however, as one of
the new Windows 95 common controls is a slider control, using scrollbars for this
purpose is no longer necessary.
REVIEW EXERCISE
DIALOGS AND
PROPERTY SHEETS 8
Applications use dialogs in many situations. The MFC Library supports dialogs through
the CDialog class and derived classes.
A CDialog object corresponds to a dialog window, the content of which is based on a
dialog template resource. The dialog template resource can be edited using any dialog
editor; typically, however, you would use the dialog editor that is part of the Developer
Studio lor this purpose.
Perhaps the most important feature of CDialog is its support for Dialog Data Exchange,
a mechanism that facilitates the easy and efficient exchange of data between controls
in a dialog and member variables in the dialog class.
The CDialog class is derived from CWnd; thus, you can use many CWnd member
functions to enhance your dialog. Furthermore, your dialog classes can have message
maps; indeed, except for the most simple dialogs, it is often necessary to add message
map entries to handle specific control messages.
Newer applications often support tabbed dialogs, or property sheets. A property sheet
is really several dialogs merged into one; the user uses tab controls to pick any one of
the property pages that comprise a property sheet.
The basic steps in constructing a dialog and making it part of your application include
creating the dialog template resource, creating a CDialog-derivcd class that corresponds
to this resource, and constructing an object of this class at the appropriate location in
your application.
Zero to Mastery Microsoft Visual in C++
For our experiments with dialogs, we use a simple App Wizard-created SD1 application
named DLG. Other than selecting the Single document application type, this application
should be created with AppWizard’s defaults.
The next section shows you how to create a simple dialog that has an editable text
field and make it part of the DLG application by connecting it to a new menu item,
View Dialog. The dialog, as displayed by DLG, is shown in Figure 8. 1.
While the dialog template is open for editing in the dialog editor, you can directly
invoke the ClassWizard to construct the dialog class corresponding to this template.
Membei hjnpiQn?;
Г' Import ая esdsting cfass.
V OnBegriPm1
f* ^elecl 5Л enisling class
V CtoDraw
V OnEndPnrtir^1
V □nPropaiePnrting
V PieCreatoWndoA1
Desai ption
At this time, the ClassWizard displays the Create New Class dialog. Here, you can
enter the dialog’s name and set other options, such as the filename, the resource
identifier, or OLE automation settings. You can also add this class to the Component
Gallery for later reuse in other applications.
Zero to Mastery Microsoft Visual in C++
Cliss rltwmatoi
M*ssa0*Map5 | K | CM>Draflog|
Nanw
Piopct. [CDialQQ
Бате dasi: Add Cla^
|dlg
File
G :W0U6VHW.ti.G
OhfEct IDs: M^iabjcw £|мпвви.
Add a suitable name for the new class, for example, CMyDialog. It may also be a
good idea to uncheck the Add to Component Gallery check box; after all, it is not
necessary to clutter the component gallery with code that is used for demonstration
purposes only.
Should you change the filenames that the ClassWizard suggests for your new class’s
header and implementation files? Should you use a separate header and implementation
file for every new dialog you create? This is an interesting question. At the surface,
the answer would appear to be a yes; then again, even the AppWizard itself violates
this “rule” when it places both the declaration and implementation of your application’s
about dialog into the application object’s implementation file. Thus, I believe ihat in
the end, it is best left to the judgment of the programmer. I often grouped dialog
classes together if they were small, simple, and related. Leaving them in separate files
tended to clutter the application workspace. However, this is less of a concern with
Visual C++ where you no longer have to use File View to access your source code;
also, using separate files makes it easier to use the Component Gallery.
Dialogs and Property Sheets
For now, leave the filenames at the ClassWizard-generated defaults: MyDialog.h and
MyDialog.cpp. Clicking on the Create button actually creates the new class and leaves
the ClassWizard main dialog open.
The next step is to add a member variable that corresponds to the edit field in the
dialog template.
De scrip-iorr
MawiniumCharactiK:
Message Maps
Project
Add difis...
Idlg
Add Variable...
G \0(д\МуРч1ов h G
Deseret Ian:
MaKFriim Characters
Cancel |
Help
II Construction
public:
Dialogs and Property Sheets
Declarations for the constructor function and an override for the DoDataExchange
member function are also provided here.
These two functions are defined in MyDialog.cpp (Listing 8.2). Notice that the
ClassWizard inserted code into both of them; the member variable m_sEdit is initialized
in the constructor and also referred to in DoDataExchange.
//{{AFX_DATA_lNlT(CMyDialog)
m_sEdit = _T(“”);
//} }AFX_DATA_1N1T
C Dialog ::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMyDialog)
DDX_Text(pDX, IDC_EDIT, m.sEdit);
//} }AFX_DATA_MAP
BEGINJ4ESSAGE_MAP(CMyDialog, CDialog)
//({AFX_MSG_MAP(CMyDialog)
// NOTE: the ClassWizard will add message map macros here
//}} AFX_MSG_MAP
END_MESSAGE_MAP()
DoDataExchange is the function that facilitates data exchange between member
variables and dialog controls. It is invoked both when the dialog is constructed and
when it is dismissed. The macros inserted by ClassWizard (such as the DDX_Text
macro) facilitate data exchange in both directions; the direction is determined by the
m_bSave A nd Validate member of the CDataExchangc object, pointed to by the pDX
parameter We revisit this function and the various data exchange helper macros shortly.
To add code that handles the new menu item, invoke the ClassWizard, and add a
command handler function for 1D_V1EW_DIALOG to the CMainFrame class. (Why
CMainFrame? Displaying this dialog has nothing to do with a specific document or
any of its views, so CMainFrame appears to be the most logical choice.) This is
accomplished most easily by right-clicking on the new Dialog menu item to invoke
the ClassWizard, selecting ClassWizard's Message lab, selecting the
1D_V1EW_DIALOG identifier, and using the Add Function button.
The implementation of CMainFrame: :OnViewDialog is shown in Listing 8.3. After
constructing the dialog object, we assign an initial value to the member variable
m_sEdit. Next, we invoke the dialog via the DoModal function. After the dialog is
dismissed by the user, we examine the new value of m_sEdit by simply displaying it
in a message box.
{
// TODO: Add your command handler code here
CMyDialog myDialog;
myDialog.DoModal();
MessagcBox(myDialog.m_sEdit);
}
Note that in order to be able to declare an object of type CMyDialog in
CMainFrame: :On Vie wDialog, it is necessary to include the MyDialog.h header file in
MainFrm.cpp.
That’s it. The application is ready to be recompiled and run.
Create(CMyDialog::lDD, pParenl);
//{{AFX_DATA_INlT(CMyDialog)
m_sEdit = _T(“”);
//} }AFX_DATA_1N1T
Why was it necessary to call UpdateData in this function? Because we set the value of
m_sEdit after the dialog box object has been constructed and initial Dialog Data
Exchange took place. By calling UpdateData, we ensure that the controls in the dialog
box object arc updated to reflect the settings in the member variables of the CDialog
object. This is yet another example that should remind us that the C++ object and the
Windows object arc two different entities.
We must also call the ShowWindow member function to make the new dialog visible.
Alternatively, we could have created the dialog box template resource with the
WS.V1SIBLE style.
Dialogs and Property Sheets
181
How long will this dialog exist? As long as the user does not dismiss it by clicking on
the OK or Cancel button. At that time, the default implementations of CDialog::OnOK
and CDialog::OnCancel hide the dialog box but do not destroy it. Obviously, we must
override these functions to properly destroy the dialog. In both of these functions, a
call must be made to the DestroyWindow member function.
We must also override the dialog’s OnOK function to ensure that we process whatever
the user entered in the dialog. We can no longer rely on the function calling DoModal
for this purpose, for the simple reason that DoModal is never called.
Calling the DestroyWindow member function from OnOK and OnCancel ensures that
the Windows dialog box object is destroyed; but how will the C++ object be destroyed?
The answer to that question is yet another override. You must override the PostNcDestroy
member function and delete the CDialog-derived object from within it.
To override the default implementations of OnOK, OnCancel, and PostNCDestroy,
you must first add these functions to the CMyDialog class through ClassWizard. Perhaps
the simplest way to do this is to open the implementation file, MyDialog.cpp, and use
the WizardBar to add the functions.
Implementations of CMyDialog::OnOKt CMyDialog::OnCancel, and
CMyDialog::PostNcDestroy are shown in Listing 8.6 (MyDialog.cpp).
void CMyDialog::OnOK()
void CMyDialog::PostNcDestroyQ
// TODO: Add your specialized code here and/or call the base class
CDialog::PostNcDestroy();
Zero to Mastery Microsoft Visual in C++
delete this;
If your modeless dialog must notify the frame, document, or view, it can do so by
calling a member function* The dialog class can have a member variable that stores a
pointer to the frame, document, or view object from within which the dialog has been
created* Other mechanisms for communication between the modeless dialog and other
parts of your application are also conceivable; for example, the dialog may post a
message to the application*
In the preceding example, we have used Dialog Data Exchange to map the contents of
an edit control to the contents о I a CString member variable in the dialog class* The
Dialog Data Exchange mechanism offers many other capabilities for mapping simple
variables or control objects to controls in a dialog box*
Note: Although Dialog Data Exchange and Dialog Data Validation are described in the context
of dialog boxes, they are not limited in use 10 dialog boxes only* The member functions discussed,
such as DoDataExchange and UpdateData, are actually member functions of CWnd, not CDialog.
Dialog Data Exchange is also used outside the context of a dialog box; CFormView and classes
derived from it are one example.
The MFC Library provides additional versions of the DDX functions to facilitate data
exchange between a dialog box and records in a database. These functions have names
that begin with DDX_Field; for example, the database variant of DDX_Text would
be named DDX_FieldText.
CDialog-derived objects are, as you might expect from CWnd-derived objects, capable
of handling messages. In fact, in all but the simplest cases, it is necessary to add
message handlers to your CDialog-derived dialog class.
Dialogs and Property Sheets
185
Message handling in a dialog is no different from message handling in a view or
frame window* Message handler functions can be easily added to the dialog class’s
message map using ClassWizard* In the earlier examples we have already done that
when we added override versions of the OnOK and OnCancel member functions*
These member functions arc handlers of WM_COMMAND messages* (The third
override function we implemented, PostNcDestroy, is not a message handler; however,
it is called from within the handler for WM_NCDESTROY messages, OnNcDestroy*)
The most frequently used message handlers in a dialog class correspond to messages
sent to the dialog window by one of its controls* These include BN_CL1CKED messages
sent by a button; the variety of CBN_ messages sent by a combo box; EN_ messages
sent by an edit control; and so on* Another set of message that dialog classes often
handle consists of WM_DRAW1TEM and WM_MEASUREITEM for owner-draw
controls*
Owner-draw controls bring up an interesting issue. Should you handle such a situation
from within your dialog class, or should you assign an object of a class derived from
a control class to the control and handle it there? For example, if you have an owner
draw button, you have the choice of adding a handler for WM_DRAWITEM messages
to your dialog class, or deriving a class from CButton and overriding its Drawitem
member function*
Property sheets are several overlapping dialogs in one. The user selects one of the
dialogs, or property pages, by clicking on the corresponding tab in a tab control*
MFC supports property sheets through two classes: C Property Sheet and C Property Page*
C Property Sheet corresponds to the property sheet; CPropertyPage-derived classes
correspond to the individual property pages within the property sheet*
Using a property sheet requires several steps. First, the properly pages must be
constructed; next, the property sheet must be created.
The following simple example reviews this process. A new application, PRP, is used
to display a properly sheet, as shown in Figure 8.8. Like our earlier application,
DLG, PRP is also a standard SD1 application created by AppWizard.
The identifier of the dialog template resource should be set to IDD_PAGE1; the
identifier of the edit control should be set to IDC_EDITL Make sure that the dialog
template’s properties are set correctly. To set the dialog’s caption, double click on the
dialog to invoke the Dialog Properties property sheet (Figure 8/10).
To set the style, border style, and titlebar setting, select the Styles tab in the property
sheet of the dialog resource (Figure 8/11).
To set the Disabled style of the dialog resource, use the More Styles tab in the dialog resource
property sheet (Figure 8.12).
The second property page in our simple example is, for the sake of simplicity, nearly
identical to the first. In fact, you can create the dialog resource for this second property
page by simply making a copy of the first Make sure that the identifier of the new
dialog resource is set to IDD_PAGE2 and that the identifier of the edit control within
it is 1DC_ED1T2. (It would be perfectly legal to use the same identifier for controls in
separate property pages; they act and behave like separate dialogs. Nevertheless, I
prefer to use distinct identifiers; this helps reduce the possibility for errors.)
Once both property page dialog resources have been constructed, it is time to invoke
the ClassWizard and construct classes that correspond to these property pages. To do
so, invoke the ClassWizard while the focus is on the first property page dialog resource
while it is open for editing. As with dialogs, the ClassWizard will recognize that the
dialog template has no corresponding class and offer you the opportunity to create a
new class.
In the Create New Class dialog, specify a name for the class corresponding to the
dialog template (for example, CMyPagel). More importantly, make sure that this
new class is based on CPropeilyPage (and not the default CDialog). Once the correct
settings have been entered, create the class.
While in ClassWizard, you should also add a member variable that corresponds to the
edit control in ihe property page. Name this variable m_sEditl.
These steps should be repeated for the second property page. The class for this property
page should be named CMyPage2t and the member variable corresponding to its edit
control should be named m_sEdit2.
Construction of our property pages is now complete. Take a brief look at the code
generated by ClassWizard. The declaration of CMyPage I is shown in Listing 8.7 (the
declaration of CMyPage2 is virtually identical).
DECLARE_DYNCREATE(CMyPage I)
// Construction
public:
CMyPage I ();
-CMyPage 1();
// Dialog Data
//{{AFX_DATA(CMy Pagel)
enum { 1DD = IDD_PAGE1 };
CString m_sEdit I;
Dialogs and Property Sheets
189
//}}AFX_DATA
// Overrides
// ClassWizard generate virtual function overrides
//{{AFX.VIRTUAL(CMyPage 1)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//} }AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CMyPagel)
// NOTE: the ClassWizard will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
As you can see, there is very little difference between this declaration and the
ClassWizard-generated declaration of a CDialog-derived dialog class. Most importantly,
CProperty Page-derived classes can use Dialog Data Exchange functions just as classes
derived from CDialog.
The implementation of CMyPage I member functions (Listing 8.8) is also no different
from the implementation of similar functions in a CDialog-derived class. Perhaps the
one notable difference is that this class has been declared as dynamically creatable
with the help of the DECLARE_DYNCREATE and IMPLEMENT.DYNCREATE
macros.
//{{AFX_DATA_INIT(CMyPagel)
m.sEditl = _TO;
//} }AFX_DATA_INIT
CMyPage 1 ::-'CMyPagel()
Zero to Mastery Microsoft Visual in C++
myPage2.m_sEdit2 = “Second”;
myPropSheet.AddPagef&myPage I);
myPropSheet.AddPage(&myPage2);
myPropSheetDoModal();
Do not forget to include the header files MyPage I .h and MyPage 2. h in MainFrm.cpp;
otherwise, you will not be able to declare objects of type CMyPagel or CMyPage2
and the function in Listing 8.9 will not compile.
At this time, the application is ready to be compiled and run.
Although in this example we made no use of the property page member variables
after the property sheet is dismissed, we could access them simply through the property
page objects myPagel and myPage2.
function to ensure that objects of this class are destroyed when the modeless properly
sheet is dismissed.
The new property sheet class can be created using ClassWizard. Create a new class
derived from CProperty Sheet, and name it CMySheet. While in ClassWizard, add the
PostNcDestroy member function.
The declaration of CMySheet (in the file MySheet.h), as generated by ClassWizard, is
shown in Listing 8.10.
DECLARE_DYNAMIC(CMySheet)
// Construction
public:
CMySheetfUINT nlDCaption, CWnd* pParcntWnd = NULL,
UINT iSelectPage = 0);
CMySheetfLPCTSTR pszCaption, CWnd* pParentWnd = NULL,
UINT iSelectPage = 0);
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMySheet)
protected:
virtual void PostNcDestroyO;
//} }AFX_VIRTUAL
// Implementation
public:
virtual -CMySheet();
//Generated message map functions
protected:
//({AFX_MSG(CMySheet)
// NOTE - the ClassWizard will add and remove member
Dialogs and Property Sheets
193
// functions here.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
// TODO: Add your specialized code here and/or call the base class
CProperlySheel::PostNcDeslroy();
for (ini i = 0; i < GetPageCountf); i++)
delete GelPage(i);
delete this;
A modeless property sheet does not have OK, Cancel, and Apply Now buttons by
default. If any buttons are required, these must be added by hand. We are not going to
worry about these now; the modeless properly sheet can still be dismissed by closing
it through its control menu.
How is the modeless property sheet invoked? Obviously, we have to modify the
On ViewPropertysheet member function in our CMainFrame class, as using DoModal
is no longer appropriate. Nor is it appropriate to create the properly sheet or any of its
property pages on the stack, as we do not want them destroyed when the
OnViewPropertysheet function returns. The new OnViewPropertysheet function is
shown in Listing 8.12.
In order for CMainFrame: :On ViewProperty sheet to compile in its new form, it is
necessary to add the include file MySheet.h to MainFrm.cpp; otherwise, the attempt
to declare an object of type CMySheet will fail
The application is now ready to be recompiled and rum
Dialogs and Property Sheets
195
Summary
In MFC, dialogs are represented by classes derived from CDialog.
The steps of constructing a dialog that is part of an MFC application are as follows:
Create the dialog template resource.
2. Invoke ClassWizard and create the dialog class corresponding to the resource.
3. Through ClassWizard, add member variables corresponding to controls.
4. Still using ClassWizard, add message handlers if necessary.
5. Add code to your application that constructs a dialog object, invokes it (through
the DoModal member function), and retrieves results.
MFC applications can also have modeless dialogs. These dialogs are constructed
differently. The constructor function in your dialog class should call the Create member
function; it should also call the modeless version of the constructor of the CDialog
base class. The modeless dialog must also explicitly be made visible by calling the
ShowWindow member function.
Classes that correspond to modeless dialogs should override the OnOK and OnCanccI
member functions and call the Destroy Window member function from within them.
They should also override PostNcDestroy and destroy the C++ object (using delete
this, for example).
Controls in a dialog are often represented by member variables in the corresponding
dialog class. To facilitate the exchange of data between controls in the dialog box
object and member variables in the dialog class, the Dialog Data Exchange mechanism
can be used. This mechanism provides a simple method for matching member variables
to controls- Member variables can be of simple value types or can represent control
objects. It is possible to simultaneously use a member variable of a simple type to
obtain the value of a control while using a control object to manage other aspects of
the control. The Dialog Data Exchange mechanism also offers data validation
capabilities.
For frequently used nonstandard types, it is possible to extend the Classwizard's
ability to handle Dialog Data Exchange. New data exchange and validation routines
can be added either on a per project basis or to your overall Visual C++ configuration.
Property sheets represent several overlapping dialogs, or property pages, which the
user can choose by clicking on corresponding tabs in a tab control.
Creating a property sheet is a two-phase process. First, property pages must be created;
second, a property sheet object must be constructed, the property pages must be added
to it, and the property sheet must be displayed.
Zero to Mastery Microsoft Visual in C++
REVIEW EXERCISE
L What is a Constructor?
(a) A function called when an instance of a class is initialized.
(b) A function that is called when an instance of a class is deleted,
(<?) A special function to change the value of dynamically allocated memory
(d) A function that is called in order to change the value of a variable.
2, A class is
(a) Data Type, (b) Abstract Type
(c) User Defined Type (d) All of these options
3, Can two classes contain member functions with the same name?
(a) No.
(b) Yes, hut only if the two classes have the same name.
(c) Yes, but only if the main program does not declare both kinds.
(d) Yes, this is always allowed.
5, Object Oriented Technology’s use of facilitates the reuse of the code and architecture
and its feature provides systems with stability, as a small change in requirements does
not require massive changes in the system:
(«) Encapsulation; inheritance (b) Inheritance; polymorphism
(O Inheritance; encapsulation (d) Polymorphism; abstraction
10, Can two classes contain member functions with the same name?
GO No.
(b) Yes, but only if the two classes have the same name,
(t?) Yes, but only if the main program does not declare both kinds.
GO Yes, this is always allowed.
11, Suppose that the Test class does not have an overloaded assignment operator. What happens when
an assignment a=b; is given for two Test objects a and b?
GO The automatic assignment operator is used, (b) The copy constructor is used.
(c) Compiler error. (d) Run time error.
14, In function overloading, functions having the same name must differ in
GO Return type. (b) Number of arguments.
GO Type of arguments. (d) Any of a,b,c are possible.
17, Inheritance is the mechanism of class by which we can inherit properties of base class to derived
class. The different forms of inheritance are as follows:
(a) Single, (ft) Multiple,
(f> Multilevel. (d) Multipath.
(e) Hybrid. (/) Hierachica 1
(g) All,
18, Which of the following are class relationships?
(a) is-а relationship. (*>) Pan-of relationship.
(c) Use-а relationship. (d) All of these options.
19, What is inheritance?
(a) It is same as encapsulation. (*) Aggregation of information
(c> Generalization and specialization. (d) All of these options.
20, Object orientated programming allows for extension of an object function or of class function. This
ability within OOP is called.
extensibility, (b) expansion capacity,
(O virtual extension. (d) scalability.
21, The ability to reuse objects already defined, perhaps for a different purpose, with modification
appropriate to the new purpose, is referred to as
(a) Information hiding, (b) Inheritance.
(c) Redefinition. (d) Overloading
25* How do you define an abstract class? In other words, what makes a class abstract?
(a) The class must not have method definitions,
(&) The class must have a constructor that takes no arguments.
(c) The class must have a function definition equal to zero.
(d) The class may only exist during the planning phase.
(e) all.
ANSWERS
IL Arrange the following in order they are searched when a DLL is loaded memory:
(«) Process current directory
(b) Directory containing exe
(O Windows directory
(d) Windows system directory
(;..f, I». A. I’-
(b) A, C, B, D
(c)B, A, C, D
(d) B, A, D, C
14* Which type of dialog box overlaps all other windows, including that of other applications when
displayed?
GO System modal (b) Modal
(c) Modeless
15, You override OnlnitDialogf) in your class derived from Cdialog then when should you call the
base class OnlnitDialogO inside your version?
GO As the first statement (b) As the last statement
(c) Anywhere (d) You don't have to call it
16* A class derived directly from which of the following would not be able to receive command
messages
(«} CObject (b) CCmdTarget
GO CWnd (d) CDocument
17* For the purpose of constructing a modeless dialog box you use following constructor
GO CDialog( LPCTSTR , CWnd® = NULL);
(£>) CDialog( UINT nIDTemplate, CWnd* pParemWnd = NULL);
(c) CDialogO;
(<0 Any of the above can be used.
19* To replace the default message loop and provide your own customized version you override
GO Onldle (b) PreTrans I a teMessage
(с) I nitinstance (d) Run
21* Which of the following message will undergo command routing in document view architecture?
GO WM_CHAR
(M WM.COMMAND
G?) WM_LBL’TTONDOWN
(d) WM.PAINT
Introduction to VC++
203
22* Worker threads are recognized by their
GO Class (b) Function
(c) Name
23* Which of the following objects cannot be used for synchronization of threads across process
boundary?
GO Semaphore (b) Mutex
(c) Event (d) Critical section
25. The first parameter that CSingleDocTemplate takes corresponds (o the ID of which of the following
resources:
GO Main menu (b) Icon
(t?) Resource string (d) All the above
26* Which of the following class is specially meent for text input and output?
GO CFile (b) CMemFile
GO CSocketFile (d) CStdioFile
27* Which of the following are not added to a class due to inclusion of DECLARE- DYNCREATE
macro?
(a) CreateObject() (b) GetRuntimeClass()
(c) CRuniimeClass object (d) IsKindOf(>
31* Which calling convention would you used if you want to use a DLL in VB.cdecl?
GO stdcall (b) fastcall
(c) Any calling convention would work*
32* How many maximum rows and columns can a static splitter window contain?
GO 3x3 (b) 2x2
GO 16 x 16 («0 There is no such limit.
33* Which class is used to have more than one view of a single document in an SD1 application?
GO CFrameWnd (b) CView
(c) CSplitterWnd GO CWnd
35* If you override OnOk () in your class derived from CDialog then when should you call the base
class OnOk() inside your version?
GO As the first statement (b) As the last statement
(c) Anywhere (d) You don't have to call it
37* If you want to perform certain initializations, anytime a document is created in SD1, you should put
the code for initialization in
GO Document class constructor (b) OnOpenDocument
G) OnDocumentFile (d) OnNewDocument
38* On which of the following synchronization object can you call Lock() multiple times without
causing the threads to block?
GO CEvent (b) C Critic al Section
GO CMutex (d) CSemaphore
39* Which of the following class does not appears in a typical dialog based application?
GO CDialog derived (b) CFrameWnd derived
(c) CWinApp derived (d) CDocument derived
UNSOLVED QUESTIONS
8. Which function in the View is first called when the View is attached to the Document?
(a) OnUpdate ( } (b) UpdateAllViews ( )
(c) OnlnitialUpdate ( } (d) OnNewDocument ( }
9 One must pass the parameter to the function UpdateAllViews ( ) in order to send
notification for all the views to he updated
(«) 1 (b) NULL
(c) this (d) None of the above,
12, The OnCreate ( ) member function of the CFrameWnd calls function of the same class
to create the view
(a) OnCreateView ( ) (b) OnCreateControl ( )
(c) OnCreateApplication ( ) (d) OnCreateClient ( )
(e) b and c.
15. In order to terminate an appliction from the OnCreate ( ) member function, one must return
(«) 0 (h) 1
(с) -1 W) 2
Introduction to VC++
207
ANSWERS
TRUE OR FALSE
1. All MFC classes are derived from CObject
2. Toolbar is child of the view window.
3. An applications top level menu can be changed at runtime.
4. A class supporting Serialization has to use DEC LAR E_S ERIAL and IMPLEMENTS ERLAL macros.
5. AfxBeginThread can create only worker threads.
6. On Pai nt and OnDraw member functions serve the same purpose.
7. Runtime class information supported by CObject is same as RTTI of C++.
8. Initlnstance is member of CWinThread class.
9. DoDataExchange() of CDialog calls UpdateData() function
10. To create a property sheet on screen we need to call the CreatePropertySheetQ function.
IL In a doeument-View based SDI application which object creates the frame window object.
12. CreateStatic is a member function of the CFrameWnd class.
13. We can access the pointer of the document in the constructor of the view.
14. In the view class OnDraw function is called by OnInitialUpdate().
15. Object of CCreateContext class is passed as parameter to PreCreateWindow.
16. The VERIFY macro is available for release build of MFC.
17. The CreateThread is a global function.
18. AfxSetResourceHandle is used for message dispatching.
19. MFC classes can be exported out of regular DLLs.
20. WaitForSingleOhject can be used for locking on a Critical section object
21. The view is created in the OnNewDocument of Document object.
22. It's a must to override the function Onldle.
23. OnSetActive is a member function of CPropertySheet.
24. C SyncObject is the base class for CSingleLock.
25. One can have more than one global object for the Application class in a project.
26. The Run( ) function of the CWinThread class can be overriden.
27. There can be more than one document class present in a SDI (Single Document Interface) application.
28. Is it necessary to create the view immediately after you create the static splitter window.
29. OnDraw ( ) is a member function also present in the CDocument class.
30. If you want to create a set of buttons on the view, you would trap the OnCreate ( ) function of the
class CMainFrame.
31. The code to be executed for a thread, is placed in the constructor of the class which is derived from
CWinThread.
32. afx_msgGetApp( ) is the API used to get a pointer to the object representing the Application.
Introduction to VC++
211
33, If the macro DECLARE_SERIAL ( ) is added in a class, you can also add other macros like
DECLARE_DYNAMIC ( ) & DECLARE_DYNCREATE( ) in the same class.
34, GetDocument( ) is a member function, overriden in your document class which returns a pointer to
the Active Document in an SDI application.
35, There can be only one CWinApp object in an MFC application. [True]
36, Runtime class information supported by CObject is same as RTTI of C++. [False]
37, MFC uses virtual functions to implement messages. [False]
38, All MFC classes are derived from CObject. [False]
39, CObject class is also available outside MFC framework. [True]
40, CRuntimeClass is derived from CObject. [False]
41, OnPaint and OnDraw functions serve the same purpose. [False]
42, You can’t call MessageBoxQ function inside Ini tin stance (). [True]
43, A window in windows application gets all the mouse messages that are generated by the user.
[False (Only command messages,)]
44, Drop-down menus are actually pop-up menus. [True]
45, If two or more items in the same menu are assigned the same shortcut key it will generate an error
when that key is pressed. [False (Focus toggles between items.)]
46, DoModal doesn’t returns until the dialog box is dismissed. [True]
47, Modeless dialog box is dismissed using EndDialog. [False (Using Destroy Window.)]
48, Property sheets support DDX and DDV. [True]
49, In document/view architecture application object and document object can receive all types of
messages. [False (Only Command messages)]
50, Toolbar is a child of view window. [False. (Frame window.)]
51, An application top-level menu can be changed at runtime. [True]
52, In an MDI application we can open several different types of documents simultaneously. [True]
53, You can omit individual sub string in a resource document string in do document/view architecture.
[True]
54, In document/view architecture OnDraw has to be overridden. [True]
55, In an SDI for each new view, a new document object is created and attached to the view.
[False. (Views are reused.)]
56, A document object can process a keyboard and mouse messages. [False]
57, An MDI can have one or many menu resources. [False (Cannot have one menu resource)]
58, In an MDI application we can have different icons for mainframe window and the child form
windows. [True]
59, In an MDI application different child frame window can have different icons. [True]
60, A static splitter must have at least one sharing scroll bar. [True]
Zero to Mastery Microsoft Visual in C++
2'
61. If we have two panes in a static splitter window, housing two views of a document, then change in
the document data automatically changes the two views.
[False. (We need to do it using CDocumentzUpdateAllViews)]
62. SDI supports only one document type. [True]
63. In a document/view architecture the application object is created dynamically. [True]
64. The drag and drop support is provide through the function RegistershellFileTypes (). [True]
65. You can call AddDocTemplete multiple times in an SDI. [False]
66. Both Windows and MFC treat UI and worker threads differently.
[False. (Windows makes no distinction between these threads)]
67. Both UI and worker threads have message loops. [False. (Only UI threads)]
68. Two or more threads may have same thread function. [True]
69. A thread function is a callback function. [True]
70. AfxBeginThread can create only worker threads. [False]
71. Critical Sections can be used for synchronization within same or different process.
[False. (Only within same process.)]
72. Events can be used for synchronization within same or different process. [True]
73. When a priority is specified for a thread it is relative to all other threads in the operating system.
[False. (Only relative to the threads in the same process)]
74. Bitmaps can be selected in any device context. [False. (Only memory device context.)]
75. When a memory device context is first created its size can be anything.
[False. (Size of one monochrome pixel.)]
76. A file that is dynamically linked can have any extension. [True]
77. Global variables in a dll are same for all the processes that are linked to that dll. [False. (Private to
each process.]
78. DlllMain () is called only once in the lifetime of a dll. [False. (Four times.)]
79. In static linking the object code of the library does not becomes part of the executable but in
dynamic linking it does. [False. (Vice-versa)J
80. Every dll must contain a DllMain () provided by the user.
[False. (The framework provides a dummy DllMain () if u donTt provide one.)]
81. An extension dll can’t be use by visual basic clients. [True]
82. It is necessary that a dll used by the client program must either be present in the client program’ s
directory or in the Windows’ system directory.
[False. (Can also be present in directory specified by ‘path’ environment variable or current
directory j |
83. Every time you make a change to dll you have to recompile the applications using the dll.
[False. (Only if you have made changes to prototypes of exported functions.)]
84. CFile class directly supports both binary and text input and output.
[False. (Only binary I/O is directly supported. Its derived classes are used for text I/O.)]
Introduction to VC++
85. Same CArchive object is used for both storing and loading an object during serialization throughout
the application lifetime. [False. (Each time a new object is created.)]
86. The CScrollView class does not support scrolling from the keyboard by default. [True]
87. Every MFC application that has a UI must have a class derived from CView.
[False. (Dialog based application don't have to.)]
88. When loading DLL windows searches the directory defined in the path environment variable
before it searches the current directory. [False. (At the end.)]
89. Dialog box messages are not passed to the main window of application. [True]
90. A tool bar is also a window that is child of the window hosts it. [True]
91. The repeat count parameter for a key-up message can never be anything but one. [True]
92. Operating system gives higher priority to UI threads as compared to worker threads.
[False (O.S makes no distinction between UI and Worker threads. The programmer decides the
priority if required.)]
93. Toolbars can also contain items that do not appear in menu. [True]
94. Toolbars cannot have Texts. [False]
95. LTt important to forward non-client area and system keyboard messages to base class if you process
them". [True]
QUESTIONS
1. SetModifiedFlagO is a member function of which class and why do we need to use it?
2. DeleteContents() is a member function of which class and why do we need to override it?
3. Which class serves as a helper class for implementing RTTI mechanism?
4. Write the preprocessor directive used to differentiate between the release and debug modes while
writing any M.F.C. application.
5. Which function can be used lor getting the address of the main frame window pointer anywhere in
a M.F.C. application?
6. Write the M.F.C. given global function for creating a thread.
7. Write any two classes, which can be used for synchronizing multiple threads in M.F.C.
8. Which member function of the frame class is actually responsible for loading the resources for the
main frame window?
9. Write the member function of the CPropertySheet class, which returns a pointer to the current
active page in it.
10. Which device context class can be used for handling WM_PAINT message in M.F.C?
11. What is the purpose of the two parameters in the BEGIN_MESSAGE_MAP (,) macro?
12. What does MFC do when you call the function DoModal() to open modal dialog window
13. What does tlAFX" stand for?
Zero to Mastery Microsoft Visual in C++
2'
14. How can we call Win32 API functions in MFC program?
15. In an MDI application, which window owns the toolbar?
16. What is the difference between “grayed” and “disabled” menu item?
17. Why must a serializable class have default constructor?
IS. Write the preprocessor directive used to differentiate between debug and release modes when
writing any MFC application.
19. Void OnDraw (CDC *pDC) is a member of which class and on what occasions does the framework
call it?
20. How can we call Win32 API functions in MFC program?
21. Which virtual function should you override if you want to filter some messages and what is its
class?
22. Name the six GDI objects.
23. What is the purpose of ON_COMMAND_RANGE macro?
24. What is the purpose of resource.h file?
25. Why is it necessary to override OnOk and OnCancel for modeless dialog box?
26. How can you set the focus on a control programmatically in a dialog box?
27. How can on iterate through all the views associated with a document? Give the function names and
their required parameters.
28. Why do we need to override OnlnitialUpdate member function of CView sometimes?
29. In what order do application, frame widow, document and view objects get created?
30. Why is it not necessary to have message map entry for the “File 1 Save” menu item?
31. Why is A fx В eg inThread instead of : :CreateThread recommended in MFC for creating a thread.
32. How can main thread and the one that it spawns communicate?
33. What is an import library?
34. Is it necessary to override InitlnstanceQ in any window based MFC application? Why
35. What is the use of _T macro?
36. Why should you call base class Onldle version if you override it?
37. What is command routing?
38. How you can define and use your own messages?
39. What are owner drawn controls?
40. How to display text in status bar?
41. How to give effect of blinking to a line?
42 How to handle communication using MFC?
43 Why MFC extension Dll’s are used?
44 How to handle application resources in DIFs?
45 What are accelerator keys?
Introduction to VC++
EXCEPTION HANDLING
I
The Win32 API supports structured exception handling. Through this mechanism,
applicationscan handle various hardware- and software-related conditions. Structured
exception handling is not to be confused with the concept of exceptions in the C++
language, which is a feature of that language. The Win32 exception handling mechanism
is not dependent on its implementation in a specific language. To avoid confusion, I
decided to follow the conventions used in Microsoft documentation and use the term
“C exception” to refer to Win32 structured exceptions, and "C++ exception” to refer
to the typed exception handling mechanism of the C++ language.
C Exceptions
What is, indeed, an exception? How do exceptions work? In order to understand the
exception handling mechanism, first take a look at the program shown in Listing 1.
Appendix I: Exception Handling
--------- ----------Z 217
Listing 1 A Program that Generates an Exception
void main(void)
int x, y;
x = 5;
y = 0;
x = x / y;
What exactly happened here? Obviously, when you attempt to divide by zero, the
processor will generate an error condition (the actual mechanism is hardware dependent
and not of our concern). This error condition is detected by the operating system,
which looks for an exception handler for the specific error condition. As no such
handler was detected, the default exception handling mechanism took over, displaying
the dialog.
Using the C exception handling mechanism, it is possible lor us to catch this exception
and handle the divide by zero condition gracefully. Consider the program shown in
Listing 2,
ini x, y;
__ try
x = 5;
y = 0;
x = x / y;
Running this program no longer produces the dialog shown in Figure 1; instead, the
message “Divide by zero error?’ is printed and the program terminates gracefully.
The block of statements following the__ try instruction is often called & guard block.
This block of statements is executed unconditionally. When an exception is raised
within the guard block, the expression following the__ except statement (often called
the filter expression) is evaluated. This expression should be an integer expression
yielding one of the following values:
Table 1: Filter expression values
creating an infinite loop by returning control to the point where the error occurs
without eliminating the conditions which caused the exception in the first place.
In the other two cases, the first thing that happens is that the guard block goes out of
scope. Any function calls that might have been interrupted by the exception are
terminated and the stack is unwound.
If the filter expression evaluates to I (EXCEPT10N_EXECUTE_HANDLER), control
is transferred to the statement block following the__ except statement.
The third filter value, 0 (EXCEPTION_CONTINUE_SEARCH), hints at the possibility
of nested exceptions. Indeed, consider the program shown in Listing 17.3. In this
program, two exceptions are generated, one for a floating-point division by zero, one
for an integer division by zero. The two exceptions are handled very differently.
printfCTnside divzerofilter\n”);
if (code == EXCEPTION_1NT_D1V1DE_BY_ZERO)
*j = 2;
printf(“Handling an integer division error.\n”);
return EXCEPT1ON_CONTINUE_EXECUTION;
void divzeroQ
double x, y;
int i, j;
__ try
x = 10.0;
у = 0.0;
i = 10;
Zero to Mastery Microsoft Visual in C++
0;
void main(void)
_controlfp(_EM_OVERFLOW, _MCW_EM);
__ try
divzero();
When an exception is raised inside the divzero function, the filter expression is
evaluated. This results in a cal] to the divzerofilter function. The function checks if
the exception was an integer division by zero exception; if so, it corrects the value of
the divisor (j) and returns the EXCEPTION JLIONTINUE.EXECUTION value, which
causes the exception handling mechanism to return control to the point where the
exception was raised. In the case of any other exceptions, divzerofilter returns
EXCEPT1ON_CONT1NUE_SEARCH; this causes the exception handling mechanism
to seek another exception handler.
This other exception handler has been installed in the main function. This handler
handles floating-point division by zero exceptions. Instead of returning to the point
where execution was interrupted, it simply prints an error message.
Running this program produces the following output:
Appendix I: Exception Handling
221
Inside divzerofilter
Handling an integer division error,
i=5
Inside divzerofiller
Floating point divide by zero error.
As you can see, both times an exception is raised, the exception filter installed in the
function divzero is activated. However, in the case of the floating-point division, the
exception remains unhandled; therefore, the exception is propagated to the next level,
the exception handler installed in the main function.
NOTE: To handle floating-point exceptions, it was necessary to call the _controlfp function.
This function can be used to enable floating-point exceptions. By default, floating-point
exceptions on the Intel architecture are disabled; Instead, the floating-point library generates
IEEE-compatible infinite results.
C Termination Handling
Closely related to the handling of C exceptions is the topic of C termination handling.
To better understand the problem of which termination handling provides a solution,
consider the program shown in Listing 4.
Zero to Mastery Microsoft Visual in C++
char *p;
printf(“al locating p\n**);
p = malloc(lOOO);
printfCWOOOOOOJ = %c\n” pl 1000000J);
print!'(“freeing p\n“);
free(p);
void main(void)
__ try
badmem();
In this program, the function badmem allocates the p character array. However, its
execution is interrupted when it refers to an invalid array element. Because of this, the
function never has a chance to free up the allocated array, as demonstrated by its
output:
allocating p
An access violation has occurred*
This problem can be solved by installing a termination handler in the badmem function,
as shown in Listing .5.
Appendix I: Exception Handling
223
Listing 5. A termination handler
^include <stdio.h>
^include <windows.h>
void badmem()
char *p;
__ try
printf(“allocating p\n”);
p = malloc(lOOO);
printf(“p[ 1OOOOOO] = %c\n”, p[1000000]);
__ finally
printf(“freeing p\n”);
free(p);
void main(void)
__ try
badmem();
freeing p
An access violation has occurred
As you can see, the instructions in the badmem function arc now enclosed in a__ try
block, which is now followed by the__ finally keyword. The__ finally keyword is
special in that the instruction block that follows it is always executed, no matter under
what circumstances the function terminates. So when badmem goes out of scope due
to the exception, the instructions in the__ finally block are given a chance to clean up
any resources that might have been allocated within this function.
{
x = 5;
У = 0;
Appendix I: Exception Handling
x = divide(x, y);
catch (int)
In this example, the function divide raises an exception о Г type int when a division by
zero is attempted. This exception is caught by the exception handler in main.
char *p;
try
catch (...)
void main(void)
try
badmemO;
catch(...)
}
Running this program produces the following output:
allocating p
freeing p
An exception was raised.
The exception handler in the function badmem plays the role of the__ finally block in
the C exception handling mechanism.
Although these examples demonstrate the power of C++ exception handling with C-
style code, the use of classes in exception handling has some obvious advantages. For
example, when the exception is thrown, an object of the type of the exception is
actually created; thus it is possible to provide additional information about the exception
in the form of member variables. Also, appropriate use of constructors and destructors
can replace the relatively inelegant resource cleanup mechanism shown in Listing 7.
can be used to catch exceptions of any type, including C exceptions. This offers a
simple exception handling mechanism like the one used in Listing 7. Unfortunately,
the ellipsis handler does not have any information about the actual type of the structured
exception.
This should be easy, you say. (Well, I certainly said that when 1 first read about the
differences between C and C++ exception handling.) Why not just catch an exception
of type unsigned int (after all, the Microsoft Visual C++ documentation states that
this is the type of C exceptions) and examine its value? Consider the program in
Listing 8:
Listing 8. Failed attempt to catch C exceptions as C++ exceptions of type unsigned int
^include <windows.h>
#include <iostream.h>
void main(void)
int x, y;
try
x = 5;
У = 0;
x = x / у;
if (e == EXCEPTION_INT_D1V1DE_BY_ZERO)
Zero to Mastery Microsoft Visual in C++
else throw;
Alas, this elegant solution is no solution at all. C exceptions can only be caught by an
ellipsis handler. But not all is lost just yet; could we not simply use the
GetExceptionCode function in the C++ catch block and obtain the structured exception
type? For example, consider the program in Listing 9.
int xt y;
try
x = 5;
У = 0;
x = x / у;
catch (...)
else throw;
As they say, nice try but no cigar. The function GetExceptionCode is implemented as
an intrinsic function and can only be called as part of the filter expression in a C
Appendix I: Exception Handling
x = x / y;
}
catchfunsigned int e)
x = 10;
У - 0;
x = dividefx, y);
except(catchall(GetExceptionCode())) {}
This approach has but one problem. When the catchall function throws a C++exception
that is not handled by a C++exception handler, it is treated as yet another C exception,
resulting in another call to catchall. This would go on forever, were it not for the test
for the value 0xE06D7363, which appears to be a magic value associated with C++
exceptions. But we are getting into seriously undocumented stuff here; (here has to be
another solution!
At this point, you might ask the obvious question: if C++ programs can use the
Microsoft C exception handling mechanism, why go through this dance at all? Why
not just use__ try and__ except and get it over with? Indeed, this is a valid solution;
however, to improve code portability, you may want to use the C++ exception handling
mechanism when possible, and localize and dependence on Microsoft extensions as
much as possible.
Translating C Exceptions
Fortunately, the Win32 API provides a function that allows a much more elegant
solution for translating a C exception into a C++exception. The name of the function
is _set_se_translator. Using this function, one can finally obtain an elegant, satisfactory
solution for translating C exceptions to C++ exceptions. An example for this is shown
in Listing 11.
x = x / y;
Appendix I: Exception Handling
231
catch(unsigned ini e)
else throw;
return x;
void main(void)
int x, y;
_set_se_translatorf se_trans I ator);
x = 10;
y = 0;
x = divide(x, y);
Summary
Win32 programmers using the C++ language must face two separate, only partially
compatible exception handling mechanisms. Win32-structured exceptions are often
generated by the operating system. These exceptions are not dependent on any language
specific implementation and are used to communicate a condition to the application’s
exception handler using a 32-bit unsigned value.
In contrast, C++ exceptions are typed expressions; the nature of the exception is often
derived from the type of the object that is used when the expression is thrown.
C programscan use the__ try and__ except keywords (which are Microsoft extensions
to the C language) to handle structured exceptions. It is possible for exception handlers
Zero to Mastery Microsoft Visual in C++
to be nested. The type of the expression is obtained by calling the Ge tExc eptionCode
function in the_ except filter expression. Depending on the value of the filter expression,
an exception may be handled by the exception handler, execution may continue at the
point where the exception occurred, or control can be transferred to the next exception
handler. An unhandled exception causes an application error.
C programs can also use termination handlers. These handlers, installed using the
__ try and__ finally keywords, can ensure that a function which is abnormally terminated
by an exception is given a chance to perform cleanup.
C++ programs use the C++ try and catch keywords to handle exceptions. The type of
the exception is declared following the catch keyword. The catch keyword with an
ellipsis declaration (...) can be used to catch all exceptions; one possible use of this
construct is to act as a termination handler, analogous to the__finally block in C
exception handling.
As C++ programs can also use C exceptions, it is possible to mix the two exception
handling mechanisms. C++ programs can catch C exceptions using an ellipsis handler.
Unfortunately, this method does not allow C++ programs to obtain the exception
code. However, C++programs can install an exception translator function, which can
be used to translate C structured exceptions into C++ typed exceptions.
APPENDIX
C++ TEMPLATES
II
INTRODUCTION
Many C++ programs use common data structures like stacks, queues and lists. A
program may require a queue of customers and a queue of messages. One could easily
implement a queue of customers, then take the existing code and implement a queue
of messages. The program grows, and now there is a need for a queue of orders. So
just take the queue of messages and convert that to a queue of orders (Copy, paste,
find, replace????). Need to make some changes to the queue implementation? Not a
very easy task, since the code has been duplicated in many places. Re-inventing source
code is not an intelligent approach in an object oriented environment which encourages
re-usability. It seems to make more sense to implement a queue that can contain any
arbitrary type rather than duplicating code. How does one do that? The answer is to
use type parameterization, more commonly referred to as templates.
C++ templates allow one to implement a generic Queue<T> template that has a type
parameter T. T can be replaced with actual types, for example, Queue<Customers>,
and C++ willl generate the class Queue<Customers>. Changing the implementation of
the Queue becomes relatively simple. Once the changes arc implemented in the template
Queue<T>, they are immediately reflected in the classes Queue<Customers>,
Queue<Messages>, and Queue<Orders>.
Templates are very useful when implementing generic constructs like vectors, stacks,
lists, queues which can be used with any arbitrary type. C++ templates provide a way
to re-use source code as opposed to inheritance and composition which provide a way
to re-use object code.
Zero to Mastery Microsoft Visual in C++
C++ provides two kinds of templates: class templates and function templates. Use
function templates to write generic functions that can be used with arbitrary types.
For example, one can write searching and soiling routines which can be used with any
arbitrary type. The Standard Template Library generic algorithms have been
implemented as function templates, and the containers have been implemented as
class templates.
CLASS TEMPLATES
T is a type parameter and it can be any type. For example, Stack<Token>, where
Token is a user defined class. T does not have to be a class type as implied by the
keyword class. For example, Stack<int>andStack<Message*>are valid instantiations,
even though int and Message* are not “classes”.
Appendix II: C++ Templates
235
Implementing Class Template Member Functions
Implementing template member functions is somewhat different compared to the
regular class member functions. The declarations and definitions of I he class template
member functions should all be in the same header file. The declarations and definitions
need to be in the same header file. Consider the following.
//В Лtemplate cclass t>class b{ public: b() ; -b() ;} ; //B.CPPtfinclude
“B.H”template cclass t>bct>::b(){}template cclass t>bct>:>b(){}/ /
MAlN.CPP#include “B.ETvoid main(){ bcint> bi ; b cfloat> bf ;}
When compiling B.cpp, the compiler has both ihc declarations and the definitions
available. At this point the compiler does not need to generate any definitions for
template classes, since there are no instantiations. When the compiler compiles main.cpp,
there are two instantiations: template class Bcint> and Bcfloatx At this point the
compiler has the declarations but no definitions!
While implementing class template member functions, the definitions are prefixed by
the keyword template. Here is the complete implementation of class template Stack:
//stack.h
#pragma once
template cclass T>
class Stack
public:
Stack(int = 10) ;
-StackO { delete [J stackPtr; }
int push(const T&);
int pop(T&); // pop an element off the stack
int isEmpty()const { return top ==-!;}
int isFull() const { return top == size - 1 ; }
private:
int size ; // Number of elements on Stack
int top ;
T* stackPtr ;
if (’isFullO)
stackPtr[-H-topJ = item ;
return I ; //push successful
if (JisEmptyO)
popValue = stackPtr[top—] ;
return 1 ; //pop successful
void main()
cout « f « ‘ ;
f += L1 ;
Program Output
Pushing elements onto fs
l.l 2.2 33 4.4 5.5
Stack Full.
Popping elements from fs
5.5 4.4 3.3 2.2 1.1
Stack Empty
Pushing elements onto is
1 23456789 10
Stack Full
Popping elements from is
10 9876 5 4321
Stack Empty
In the above example we defined a class template Stack. In the driver program we
instantiated a Stack of float (FloatStack) and a Stack of int(lntStack). Once the template
classes are instantiated you can instantiate objects of that type (for example, fs and is.)
A good programming practice is using typedef while instantiating template classes.
Then throughout the program, one can use the typedef name. There are two advantages:
• typedef’s are very useful when “templates of templates” come into usage. For
example, when instantiating an STL vector of int’s, you could use:
• typedef vectorci nt, allocate rcint> > 1NTVECTOR ;
• II ihe template definition changes, simply change the typedef definition. For
example, currently the definition of template class vector requires a second
parameter.
- typedef vectorci nt, al locatorci nt> > 1NTVECTOR ;
- INTVECTOR vil ;
In a future version, the second parameter may not be required, for example,
typedef vectorcinO INTVECTOR ;
INTVECTOR vil ;
Imagine how many changes would be required if there was no typedef!
Function Templates
To perform identical operations for each type of data compactly and conveniently,
use function templates. You can write a single function template definition. Based on
the argument types provided in calls to the function, the compiler automatically
Appendix II: C++ Templates
instantiates separate object code functions to handle each type of call appropriately.
The STL algorithms are implemented as function templates.
return a > b ? a : b ;
return a > b ? a : b ;
void main()
Program Output
max(10, 15) = 15
max(*k\ 4s’) = s
max(10Js 15.2) = 15.2
Template Instantiation
When the compiler generates a class, function or static data members from a template,
it is referred to as template instantiation.
• A class generated from a class template is called a generated class,
• A function generated from a function template is called a generated function.
• A static data member generated from a static data member template is called a
generated static data member.
The compiler generates a class, function or static data members from a template when
it sees an implicit instantiation or an explicit instantiation of the template.
I. Consider the following sample. This is an example ol implicit instantiation of a
class template.
2. template cclass T>
3. class Z
4. {
5. public:
6. Z() {} ;
7* ~Z() {} ;
8. void f(){} ;
9. void g(){} ;
10. } ;
II.
12. int main()
13. {
14. Z<int> zi; //implicit instantiation generates class Z<int>
15. Z<float> zf ; //implicit instantiation generates class Zcfloat>
16. return 0 ;
17. }
18. Consider the following sample. This sample uses the template class members
Z<T>::f() and Z<T>::g().
19. template cclass T>
Appendix II: C++ Templates
20. class Z
21. {
22. public:
23. Z() {} ;
24. ~Z() {} ;
25. void f(){} ;
26. void g(){} ;
27. } ;
28.
29. int main()
30. {
31. Z<int> zi; //implicit instantiation generates class Z<int>
32. zi.f(); //and generates function Z<int>::f()
33. Z<float> zf ; //implicit instantiation generates class Z<float>
34. zf.g(); //and generates function Z<float>::gO
35. return 0 :
36. }
This time in addition to the generating classes Z<int> and Z<float>, with constructors
and destructors, the compiler also generates definitions for Z<int>: :f() and
Z<float>::g(). The compiler does not generate definitions for functions, nonvirtual
member functions, class or member class that does not require instantiation. In this
example, the compiler did not generate any definitions forZ<int>::g()and Z<float>::f(),
since they were not required.
37. Consider the following sample. This is an example of explicit instantiation of a
class template.
38. template cclass T>
39. class Z
40. {
41. public:
42. Z() {} ;
43. ~Z() {} ;
44. void f(){} ;
45. void g(){} ;
46. } ;
47.
48. int main()
242 Zero to Mastery Microsoft Visual in C++
49. {
50. template class Z<int> ; //explicit instantiation of class Z<inl>
51 * template class Z<float> ; //explicit instantiation of
52. //class Z<lloat>
53. return 0 ;
54. }
55. Consider the following sample. Will the compiler generate any classes in this
case? The answer is NO*
56* template cclass T>
57* class Z
58. {
59* public:
60* Z() (};
6L ~Z() {} ;
62* void f()|} ;
63. void g(){} ;
64. } ;
65.
66* int main()
67* {
68* Z<int>* p_zi ; //instantiation of class Z<int> not required
69* Z<float>* p_zf ; //instantiation of class Z<float> not required
70* return 0 ;
71* }
This time the compiler does not generate any definitions! There is no need for any
definitions* It is similar to declaring a pointer to an undefined class or struct*
72* Consider the following sample* This is an example of implicit instantiation of a
function template*
73* //max returns the maximum of the two elements
74* template cclass T>
75* T max(T a, T b)
76. {
77* return a > b ? a : b ;
78* }
79* void main()
Appendix II: C++ Templates
243
80.
81. int 1 ;
82. I - max(10, 15) ; //implicit instantiation of maxfint, int)
83. char c ;
84. c = max(‘k’, ‘s’) ; //implicit instantiation of maxfchar, char)
85.
In this case the compiler generates functions max(int, int) and maxfchar, char). The
compiler generates definitions using the template function max.
86. Consider the following sample. This is an example of explicit instantiation of a
function template.
87. template cclass T>
88. void Test(T r_t)
89.
90.
91.
92. int main()
93.
94. //explicit instantiation of Test(int)
95. template void Testcint>(int) ;
96. return 0 ;
97.
Note: Visual C++ 5.0 does not support this syntax currently. The above sample causes compiler
error Cl001.
public:
Zero to Mastery Microsoft Visual in C++
template о
class stream<char>
public:
void f() { cout « “stream<char>::f()”« endl ;}
} ;
int mainO
{
stream<int> si ;
stream<char> sc ;
sLf() ;
sc,f() ;
return 0 ;
Program Output
stream<T>::fO
stream<char>::f()
In the above example, stream<char> is used as the definition of streams of chars;
other streams will be handled by the template class generated from the class template.
//partial specialization
temp late ctypename Tl>
class X<TI. inl>
Appendix II: C++ Templates
245
} ■ //C2989 here
int main()
A partial specialization matches a given actual template argument list if the template
arguments of the partial specialization can be deduced from the actual template argument
list.
Note: Visual C++ 5.0 does not support template class partial specialization. The above sample
causes compiler error C2989: template class has already been defined as a non-template class.
return a > b ? a : b ;
int mainO
Program Output
max(10, 15) = 15
max(‘k\ *s*) = s
max(lOJ, 152) = 15.2
max(“ Aladdin”, “Jasmine”) = Aladdin
Not quite the expected results! Why did that happen? The function call max(“ Aladdin”,
“Jasmine”) causes the compiler to generate code formax(char*, char*), which compares
the addresses of the strings! To correct special cases like these or to provide more
efficient implementations for certain types, one can use template specializations. The
above example can he rewritten with specialization as follows:
include <iostream>
^include <cstring>
using namespace std;
//max returns the maximum of the two elements
template cclass T>
T max(T a, T b)
{
return a > b ? a : b ;
}
// Specialization of max for char*
template о
char* max(char* a, char* b)
{
return slrcmp(a, b) > 0 ? a : b ;
int mam()
Appendix II: C++ Templates
Program Output
max(10, 15) = 15
max(*k\ *s*) = s
max(10.1s 15.2) = 15.2
max(“Aladdin”, “Jasmine”) = Jasmine
Template Parameters
1. C++ templates allow one to implement a generic Queue<T> template that has a
type parameter T. T can be replaced with actual types, lor example,
Queue<Customers>, and C++ will generate the class Qucue<Customers>. For
example,
2. template cclass T>
3. class Stack
4.
5.
Here T is a template parameter, also referred to as type-parameter.
6. C++ allows you to specify a default template parameter, so the definition could
now look like:
7. template cclass T = float, int elements = 100> Stack { ♦♦.♦} ;
Then a declaration such as
Stacko mostRecentSalesFigures;
would instantiate (at compile time) a 100 element Stack template class named
mostRecentSalesFigures of float values; this template class would be of type Stackcfloat,
lOOx
Note, C++ also allows non-type template parameters. In this case, template class
Stack has an int as a non-type parameter.
Zero to Mastery Microsoft Visual in C++
If you specify a default template parameter for any formal parameter, the rules are the
same as for functions and default parameters. Once a default parameter is declared all
subsequent parameters must have defaults.
8. Default arguments cannot be specified in a declaration or a definition of a
specialization. For example,
9. template cclass T, int size>
10. class Stack
IL {
12. } ;
13.
14. //error C2989: ‘Stack<int,10>’ : template class has already been
15. //defined as a non-template class
16. template <class T, int size = 10>
17. class Stack<intT 10>
18. {
19. 1 ;
20.
21. int main()
22. {
23. Stack<float,10> si ;
24. return 0 ;
25. }
26. A type-parameter defines its identifier to be a type-name in lhe scope of the
template declaration, and canot be re-declared within its scope (including nested
scopes). For example,
27. template <class T, int size>
28. class Stack
29. {
30. int T ; //error type-parameter re-defined.
31. void f()
32. {
33. char T ; //error type-parameter re-defined.
34. }
35. } ;
36.
37. class A {} ;
Appendix II: C++ Templates
249
38* int main()
39. {
40. Stack<AJO> si
41* return 0 ;
42. }
Note: VC++ 5.0 or SP1 compiles this sample without any errors. It does not flag the re-definition
of type-para meter as an error.
43. The value of a non-type-parameter cannot be assigned to or have its value changed.
For example,
44. template <class T, int size>
45. class Stack
46.
47. void f()
48.
49. //error C2105: s++’ needs l value
50. size++ ; //error change of template argument value
51.
52.
53.
54.
55. int mainQ
56.
57. Stack<double,10> si
58. return 0 ;
59.
60. A template-parameter that could be interpreted as either a parameter-declaration
or a type-parameter, is taken as a type-parameter For example,
61. class T (} ;
62. int i ;
63.
64. template <class T, T i>
65. void f(T t)
66.
67. T tl = i ; //template arguments T and i
68. ::T t2 = ::i ; //globals T and i
Zero to Mastery Microsoft Visual in C++
int mainO
Note: Compiling the above sample using VC++ 5.0 and SP1 causes compiler error C2783: could
not deduce template argument lor i’* To workaround the problem, replace the call to f(‘s’) with
fcchar, £s*>(£s*)’
class T (} ;
ini i ;
int mainO
Program Output
xi.s = 10
xc+s = Hello
40. Each instantiation of a function template has it’s own copy of the static variable.
For example,
41. tfinclude <iostream>
42. using namespace std;
43.
44. template cclass T>
45. void f(T t)
46. {
47. static T s = 0;
48. s = t;
49. cout « “s = “ « s « endl ;
50. }
51.
52. int main()
53. {
54. f(10) ;
55. f(“Hello”) ;
56.
57. return 0 ;
58. }
Appendix II: C++ Templates
Program Output
s = 10
s = Hello
Here f<int>(int) has a static variable s of type int, and f<char*>(char*) has a static
variable s of type char*.
(л J Class View, 5
Class Wizard, 15
Abstract data type, 22
Classes, 27
Abstraction, 21
Classwizard, 173
Access specifiers, 42
Color Dialog, 158
ActiveX, 3
Combo Boxes, 167
ActiveX controls, 106
Common Dialogs, 156
ActiveX controls, 3, 9
Common dialogs, 156
Ad-hoc polymorphism, 82
Compilation error, 48
Appwizard, 107
Component Object Model, 3
Appwizard, 9
Constructor, 28
Appwizard, 98
Constructor, 50
сю Constructors, 29
Base class, 60 Control, 165
Binding, 83 Control object, 184
Buttons, 166 Copy assignment operator, 50
Copy constructor, 32
fc") Copy constructor. 33, 35
Cdialog class, 171
Child windows, 136 сю
Component Object Model, 3 Data hiding, 22
Class, 19 Debugger Windows, 104
Class, 23 Decoupling, 23
Zero to Mastery Microsoft Visual in C++
Default constructor, 64
Derived class, 63
Hierarchical, 69
Derived classes, 60
Hot Key Control, 168
Desktop window, 135
Hybrid, 69
Destructor, 29
Destructors, 84
De veloper Studio, 2 Inheritance, 21
Dialog Boxes, 154 Inheritance, 60
Dialog Class, 173 Inline function, 40
Dialog controls, 150 Inline Function, 39
Dialog Data Exchange, 182 Integrated development environment (IDE), 1
Dialog Data Validation, 182 Interface, 22
Dialog Templates, 156
Dlls, 150
Keyword, 37
Document class, 114
Dynamic, 23
Dynamic binding, 23 List Boxes, 167
Dynamic binding, 83 List Controls, 167
E
Edit Controls, 166 Message Boxes, 155
Encapsulation, 22 Message handling, 185
Events, 2 Methods, 22
Events, 2 MFC, 2
Extended window styles, 141 MFC functions, 106
MFC Library, 171
F
Microsoft .NET Framework, 1
File View, 5 Microsoft Active Template Library (ATL), 3
Font Selection Dialog, 159 Microsoft Developer Studio, 107
Function name overloading, 82 Microsoft Foundation Class, 1
Function overloading, 43 Microsoft Foundation Classes, 106
Microsoft Foundation Classes (the MFC), 3
Microsoft Visual C++, 1
Global Subclassing, 149
Microsoft Visual C++, 106
GUI (graphical user interface) programs, 3
Microsoft Windows API, 1
Index
-----------------
257
Microsoft Windows API, 1 Property sheets, 171
Modal Dialogs, 154 Property sheets, 185
Modeless Dialogs, 155 Protected members, 24
Modeless Dialogs, 179 Public, 68
Modeless Property Sheets, 191 Public keyword, 63
Multilevel, 69 Pure Virtual Function, 92
Multilevel Inheritance, 73
R
Multipath, 69
Multiple, 69 Resource View, 5
OLE Common Dialogs, 165 Single document interface (or SDI), 113
OR Visit
freebie.kartbucket.com