Professional Documents
Culture Documents
2. Describe the theory with programming examples the selection control statements in C++.
4. Describe and Demonstrate the concept of Pass by Value and Pass By Reference using
appropriate programming examples of your own.
6. Describe the Friend functions and friend classes with programming examples.
8. Explain the concept of class templates in C++ with some real time programming examples.
August 2010
Master of Computer Application (MCA) – Semester 2
MC0066 – OOPS using C++ – 4 Credits
(Book ID: B0681 & B0715)
Assignment Set – 2 (40 Marks)
Answer:
Constructors
It is possible to define and at the same time initialize objects of a class. This is
supported by special member functions called constructors. A constructor always
has the same name as the class itself. It never has an explicit return type. For
example,
class Point {
int xVal, yVal;
public:
Point (int x,int y) {xVal = x; yVal = y;} // constructor
void OffsetPt (int,int);
};
is an alternative definition of the Point class, where SetPt has been replaced by a
constructor, which in turn is defined to be inline.
Now we can define objects of type Point and initialize them at once. This is
in fact compulsory for classes that contain constructors that require arguments:
Point pt1 = Point(10,20);
Point pt2; // illegal!
The former can also be specified in an abbreviated form.
Point pt1(10,20);
A class may have more than one constructor. To avoid ambiguity, however,
each of these must have a unique signature. For example,
class Point {
int xVal, yVal;
public:
Point (int x, int y) { xVal = x; yVal = y; }
Point (float, float); // polar coordinates
Point (void) { xVal = yVal = 0; } // origin
void OffsetPt (int, int);
};
Point::Point (float len, float angle) // polar coordinates
{
xVal = (int) (len * cos(angle));
yVal = (int) (len * sin(angle));
}
offers three different constructors. An object of type Point can be defined using
any of these:
Point pt1(10,20); // cartesian coordinates
Point pt2(60.3,3.14); // polar coordinates
Point pt3; // origin
The Set class can be improved by using a constructor instead of EmptySet:
class Set {
public:
Set (void) { card = 0; }
//...
};
This has the distinct advantage that the programmer need no longer remember to
call EmptySet. The constructor ensures that every set is initially empty.
The Set class can be further improved by giving the user control over the
maximum size of a set. To do this, we define elems as an integer pointer rather
than an integer array. The constructor can then be given an argument which
specifies the desired size. This means that maxCard will no longer be the same for
all Set objects and therfore needs to become a data member itself:
class Set {
public:
Set (const int size);
//...
private:
int *elems; // set elements
int maxCard; // maximum cardinality
int card; // set cardinality
};
The constructor simply allocates a dynamic array of the desired size and
initializes maxCard and card accordingly:
Set::Set (const int size)
{
elems = new int[size];
maxCard = size;
card = 0;
}
It is now possible to define sets of different maximum sizes:
Set ages(10), heights(20), primes(100);
It is important to note that an object’s constructor is applied when the object is
created. This in turn depends on the object’s scope. For example, a global object
is created as soon as program execution commences; an automatic object is
created when its scope is entered; and a dynamic object is created when the new
operator is applied to it.
Destructors
Just as a constructor is used to initialize an object when it is created, a destructor is
used to clean up the object just before it is destroyed. A destructor always has the
same name as the class itself, but is preceded with a ~ symbol. Unlike constructors,
a class may have at most one destructor. A destructor never takes any arguments
and has no explicit return type.
Destructors are generally useful for classes which have pointer data members
which point to memory blocks allocated by the class itself. In such cases it is
important to release member-allocated memory before the object is destroyed. A
destructor can do just that.
For example, our revised version of Set uses a dynamically-allocated array
for the elems member. This memory should be released by a destructor:
class Set {
public:
Set (const int size);
~Set (void) {delete elems;} // destructor
//...
private:
int *elems; // set elements
int maxCard; // maximum cardinality
int card; // set cardinality
};
Now consider what happens when a Set is defined and used in a function:
void Foo (void)
{
Set s(10);
//...
}
When Foo is called, the constructor for s is invoked, allocating storage for
s.elems and initializing its data members. Next the rest of the body of Foo is
executed. Finally, before Foo returns, the destructor for s is invoked, deleting the
storage occupied by s.elems. Hence, as far as storage allocation is concerned, s
behaves just like an automatic variable of a built-in type, which is created when its
scope is entered and destroyed when its scope is left.
In general, an object’s constructor is applied just before the object is
destroyed. This in turn depends on the object’s scope. For example, a global
object is destroyed when program execution is completed; an automatic object is
destroyed when its scope is left; and a dynamic object is destroyed when the
delete operator is applied to it.
C++ is rich in its data types. Inheritance is the concept to inherit the properties of one class to another class. This has also
known as class structure again. For example, classes A contains two-member function ads and subtracts and class b contain
3. Multiple Inheritances:
In this type of inheritance, number of classes has inherited in a single class. Where two or more classes are, know as base class
and one is derive class.
4. Hierarchical Inheritance:
This type of inheritance helps us to create a baseless for number of classes and those numbers of classes can have further their
branches of number of class.
5. Hybrid Inheritance:
In this type of inheritance, we can have mixture of number of inheritances but this can generate an error of using same name
function from no of classes, which will bother the compiler to how to use the functions. Therefore, it will generate errors in the
program. This has known as ambiguity or duplicity.
OBJECT:-
Let's say that we are writing a text-based medieval video game. Our video game will
have two types of characters: the players and the monsters. A player has to know the
values of certain attributes: health, strength, and agility. A player must also know
what type of weapon and what type of armor they possess. A player must be able to
move through a maze, attack a monster, and pick up treasure. So, to design this
"player object", we must first separate data that the player object must know
from actions that the player must know how to execute. The definition for a player
object could be:
Player Object:
data:
health
strength
agility
type of weapon
type of armor
actions:
move
attack monster
get treasure
END;
Pointers
Every storage location of memory has an associated address. Address is a number that grows sequentially. For every program
placed in memory, each variable or function in the program has an associated address.
Pointer is a variable that stores a memory address. that is simple ! But, what is a memory address then? Every variable is located under unique location within a
computer's memory and this unique location has its own unique address, the memory address. Normally, variables hold values such as 5 or “hello” and these
values are stored under specific location within computer memory. However, pointer is a different beast, because it holds the memory address as its value and
has an ability to “point” ( hence pointer ) to certain value within a memory, by use of its associated memory address.
3. Describe the theory behind Virtual Functions and Polymorphism along with suitable
programming examples for each.
Answer:
Virtual Functions
Consider another variation of the ContactDir class, called SortedDir, which
ensures that new contacts are inserted in such a manner that the list remains sorted
at all times. The obvious advantage of this is that the search speed can be improved
by using the binary search algorithm instead of linear search.
The actual search is performed by the Lookup member function. Therefore we
need to redefine this function in SortedDir so that it uses the binary search
algorithm. However, all the other member functions refer to
ContactDir::Lookup. We can also redefine these so that they refer to
SortedDir::Lookup instead. If we follow this approach, the value of inheritance
becomes rather questionable, because we would have practically redefined the
whole class.
What we really want to do is to find a way of expressing this: Lookup should
be tied to the type of the object which invokes it. If the object is of type
SortedDir then invoking Lookup (from anywhere, even from within the member
functions of ContactDir) should mean SortedDir::Lookup. Similarly, if the
object is of type ContactDir then calling Lookup (from anywhere) should mean
ContactDir::Lookup.
This can be achieved through the dynamic binding of Lookup: the decision as
to which version of Lookup to call is made at runtime depending on the type of the
object.
In C++, dynamic binding is supported by virtual member functions. A member
function is declared as virtual by inserting the keyword virtual before its
prototype in the base class. Any member function, including constructors and
destructors, can be declared as virtual. Lookup should be declared as virtual in
ContactDir:
class ContactDir {
//...
protected:
virtual int Lookup (const char *name);
//...
};
Only nonstatic member functions can be declared as virtual. A virtual function
redefined in a derived class must have exactly the same prototype as the one in the
base class. Virtual functions can be overloaded like other member functions.
Example shows the definition of SortedDir as a derived class of
ContactDir.
#include <fstream>
#include <iostream>
#include <iterator>
#include <algorithm>
void copy_file(const std::string& name1, const std::string& name2)
{
std::ifstream in(name1.c_str());
std::ofstream out(name2.c_str());
std::copy( std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(out));
}
int main()
{
copy_file("test.txt", "test.out");
print_file("test.txt");
}
Answer:
Function Template
A function template definition (or declaration) is always preceded by a template
clause, which consists of the keyword template and a list of one or more type
parameters. For example,
template <class T> T Max (T, T);
declares a function template named Max for returning the maximum of two objects.
T denotes an unspecified (generic) type. Max is specified to compare two objects
of the same type and return the larger of the two. Both arguments and the return
value are therefore of the same type T. The definition of a function template is very
similar to a normal function, except that the specified type parameters can be
referred to within the definition. The definition of Max is shown below
template <class T>
T Max (T val1, T val2)
{
return val1 > val2 ? val1 : val2;
}
6. Discuss the theory of Exception specifications with suitable code snippets for each.
Several years after the addition of exception handling, the standardization committee added a complementary feature called exception
specifications. In this article, I will present exception specifications and question their usefulness.
The Problem
know whether this function throws an exception and under what conditions this happens? Obviously, this declaration doesn't provide any
clue. validate() could be a function that throws or it even could be a C function that's totally unaware of C++ exceptions.
Exceptions were added to C++ in 1989. Several years of confusion convinced the standardization committee to add exception specifications to the
language.
An exception specification describes which exceptions a function is allowed to throw. Exceptions not listed in an exception specification should not
be thrown from that function. An exception specification consists of the keyword throw after the function's parameter list, followed by a list of
potential exceptions:
An exception specification isn't considered a part of a function's type. Therefore, it doesn't affect overload resolution. That said, pointers to
pf is a pointer to a function that may throw string or int. You can assign to a function whose exception specification is as restrictive as, or more
restrictive than pf's exception specification. An exception specification A is said to be more restrictive than an exception specification B if the set of
exceptions A contains is a subset of B's exceptions. In other words, A contains every exception in B but not vice versa:
An overriding virtual cannot extend the set of exceptions declared in the base class's function. However, it can narrow it.
Let's look at a concrete example. Suppose you have the following class hierarchy and its related set of exception classes:
class clock_fault{/*..*/};
class Exception {/*..*/}; //base for other exceptions
class hardware_fault: public Exception {/*..*/};
class logical_error: public Exception {/*..*/};
class invalid_protocol: public Exception{/*..*/};
class RemovableDevice
{
public:
virtual int
connect(int port) throw(hardware_fault, logical_error);
virtual int
transmit(char * buff) throw(invalid_protocol);
};
RemovableDevice::connect() has an exception specification that allows it to throwhardware_fault and logical_error exceptions (as well as
any exceptions derived from these classes). The overriding Scanner::connect() narrows this specification. Throwing any exception
The exception specification of Scanner::transmit() is ill-formed. It includes clock_fault, an exception that doesn't appear in the exception
specification of RemovableDevice::transmit(). If you try to compile this code, your compiler will complain about a conflicting exception
specification.
A function with no exception-specification allows all exceptions. A function with an empty exception specification doesn't allow any exceptions:
class File
{
public:
int open(FILE *ptr); //may throw any exception
int close(FILE *ptr) throw(); //doesn't throw
};
When you declare an empty exception specification, always check that there is no risk of violating it.
Exception Specification Enforcement
Exception specifications are enforced at runtime. When a function violates its exception specification, std::unexpected() is
called. unexpected() invokes a user-defined function that was previously registered by calling std::set_unexpected(). If no function was
registered withset_unexpected(), unexpected() calls std::terminate() which aborts the program unconditionally.
Seemingly, exception specifications are the best thing since sliced bread. Not only do they clearly document a function's exception policy, but C++
At first, the C++ community greeted them enthusiastically. Many tutorials and illustrious authors started to use them all over the place. A typical class
class Foo
{
public:
Foo() throw();
~Foo() throw();
Foo(const Foo& ) throw();
Foo& operator=(const Foo &) throw()
//...etc., etc.
};
It didn't take long before programmers realized that exception specifications were rather problematic. Exceptions are dynamic. It isn't always possible
to predict which exception will actually be thrown at runtime. What happens if a function g() with an exception specification calls another
function f() that has a less restrictive exception specification or no exception specification at all?
void f();
void g() throw(X)
{
f(); //OK, but problematic
}
g() might violate its exception specification if f() throws any exception other than X.
Performance is another problem. Exceptions already incur performance overhead. Enforcing exception specifications incurs additional overhead,
For these reasons and others, exception specifications soon lost their appeal. Today, you'd hardly find them in new code or textbooks (ironically, the
textbooks that were the first to adopt them were also the first to silently withdraw them).
Summary
Exception specifications are one of those features that seemed promising in theory but proved unsavory in the real world. You might wonder why I've
spent our time discussing them. There are two reasons: first, legacy code using exception specifications still exists. Reading this code and more
Second, exception specifications teach us a lesson in programming languages design. Quite a few programming languages adopted the C++
exception handing model, including exception specifications. By the time the C++ community realized that exception specifications weren't that great,
those languages had already gotten stuck with this feature for good.
Today, there's pressure to add finally to C++. This construct is rather useful in Java, which doesn't have destructors. In C++ however, finally is
redundant since you can perform unconditional cleanup operations in the event of an exception in a destructor. So why was finallyproposed?
Simply because Java programmers still think in Java when they're writing C++ code. Exception specifications teach us to be very cautious with
Instance Diagrams
An instance diagram shows, in hexagonal boxes, the actual instances of classes
which are required for a particular artefact. It also shows the roles they play and the
relationships between them. The class hierarchy relationships of the classes can also
be shown.
B) Sequence Diagrams
Sequence Diagrams
A sequence diagram is a form of interaction diagram which shows objects as lifelines running down the page, with their interactions over time
represented as messages drawn as arrows from the source lifeline to the target lifeline. Sequence diagrams are good at showing which objects
communicate with which other objects; and what messages trigger those communications. Sequence diagrams are not intended for showing complex
procedural logic.
C) Collaboration Diagrams
A collaboration diagram, also called a communication diagram or interaction diagram, is an illustration of the relationships and interactions
among software objects in the Unified Modeling Language (UML). The concept is more than a decade old although it has been refined as modeling paradigms
have evolved.
A collaboration diagram resembles a flowchart that portrays the roles, functionality and behavior of individual objects as well as the overall operation of the
system in real time. Objects are shown as rectangles with naming labels inside. These labels are preceded by colons and may be underlined. The relationships
between the objects are shown as lines connecting the rectangles. The messages between objects are shown as arrows connecting the relevant rectangles
along with labels that define the message sequencing.
Collaboration diagrams are best suited to the portrayal of simple interactions among relatively small numbers of objects. As the number of objects and
messages grows, a collaboration diagram can become difficult to read. Several vendors offer software for creating and editing collaboration diagrams.