Sie sind auf Seite 1von 22

February 2010

Master of Computer Application (MCA) –


Semester 2
MC0066 – OOPS using C++
Assignment Set – 2

1. Explain the concept of operator overloading with


suitable examples for each.

Ans –

Operator overloading is an interesting feature of C++ that allows


programmers to specify how various arithmetic, relational and many
other operators work with user defined datatypes or classes. It provides
a flexible way to work with classes and can make program code look
obvious. In the unit 5, we had written an add function for the class
distance. To perform addition of two distance objects we used a call
d3.add(d1,d2). Instead of such statements it would be more clear if we
could use statements like d3=d1+d2. This is possible only if we inform
compiler about how + operator works with distance class. This is exactly
what operator overloading feature in C++ does. It helps to use the
default operators with the user defined objects for making the code
simpler.

However there are several problems with operator overloading


which you should be aware of. When using operator overloading, the
operator should perform only the most obvious function. Otherwise it will
lead to more confusion. If you are overloading + operator for distance
class it should add two distance objects, and should not do something
else. However some syntactical characteristics of operators cannot be
changed even if you want to. For example, you cannot overload a binary
operator to be a unary operator and vice versa.

Several operators such as dot operator, scope resolution (::)


operator, conditional operator (?:) etc cannot be overloaded.

Therefore operator overloading should be used for a class where it is


required to perform obvious functions with the default operators and
there can be no other meaning for the same.

2. Describe the following:


a) Types of Inheritance
Ans –

·Inheritance is a very powerful feature of object oriented


programming. It allows reuse of code without modifying the original
code. It also allows flexibility to programmer to make modifications to
the program without altering the original code which saves debugging
and programming time and effort.

Inheritance feature has enabled to distribute class libraries which


allows the programmer to use the standard code developed by some
another company. Inheritance is the process of creating new classes
known as derived classes from the existing or base class. The features of
the base class are said to be inherited by the derived class. The child
class has all the functionality of the parent class and has additional
functionalities of its own.

The data members in a class are usually declared private. But if


the class will be inherited by another class, then data members that will
be inherited will have to be declared as protected. Protected access
specifier allows data members to be accessed by the member functions
of inherited class but does not allow to be accessed from external
programs. Private members cannot be accessed by the inherited class
member functions. The accessibilty of base class members are also
dependent on the type of inheritance: private or public. The public
inheritance is commonly used which is shown in the program
inheritance.cpp discussed next. The types of inheritance are discussed in
the next section.

Hierarchial Inheritance

several classes derived from a single base class

Multilevel Inheritance

Inheritance can also be multilevel inheritance where another class is


derived from the derived class. In such case the grand child class inherits
all the properties of child and parent classes.
Multiple Inheritance

The derived class can also have multiple parents which is known as
multiple inheritance. Here the child or derived class has two or more
parent classes. The child class inherits all the properties of all its parents.
Multiple inheritance is implemented same as single inheritance except
that both the parent names have to be specified while defining the class.
We will discuss multiple inheritance in detail in the next unit.

In the above example we have created a class employee and a student.


Manager class is defined from both of these classes. This is useful in
instances when you want to create an employee whose educational
qualifications need not be stored such as a worker.

b) Objects and Pointers

Ans –

Pointers can point to userdefined objects as well. If we would like to define a pointer to an
object of class employee, we would use the following statement:

employee *ptr;

where ptr is a pointer pointing to object of employee class.

To access the member functions through the pointer there is simple operator -> which is
known as membership access operator. This can be used instead of dot operator when using
pointers to access the member data or functions.

The following statement accesses member function display of the employee class through
the pointer ptr:

ptr->display();

Another important use of pointers is creating datastructures like linked list. Linked list is a
dynamic data structure which can be imagined as set of nodes. Every node stores data and
link to the next node in the list. The following example implements the node using a
structure and linklist using class that stores pointer to the first node in the list.

//linklist.cpp
# include<iostream.h>
# include<conio.h>
struct node
{
int data;
node* next; // pointer to next node
};
class list
{ private:
node *first; // pointer to the first node in the list
public:
list() // no-argument default constructor
{ first=NULL;} // empty list : no first node
void additem(int d) // adds new node to the beginning of the list
{
node* newnode; // node pointer
newnode = new node; // create a new node
newnode->data=d; //assign value
newnode->next=first; // assign pointer to the first
first=newnode; // first now points to new node
}
void display() // displays all nodes
{ node* temp; // pointer to node
temp=first;
while (temp!=NULL)
{ cout<< endl<<temp->data; // print data
temp=temp->next; // points to next node
}
}
};
void main()
{ clrscr();
list list1;
list1.additem(25);
list1.additem(50);
list1.additem(52);
list1.display();
getch();
}

Every object created has a unique pointer created by default known as this pointer.This
pointer is a default pointer created for every object that points to the object itself. All the
member functions have access to “this pointer”. It is useful to return the object itself after
modification. The following program shows the use of this pointer to overload assignment
operator =:

# include<iostream.h>
class alpha
{ private:
int data;
public:
alpha() {data=0;}
alpha (int d) {data=d;}
alpha& operator = (alpha& a)
{ data= a.data;
return *this;}
};
void main()
{alpha a1(37), a2;
a2=a1;
a2.display();
}

3. Describe the theory behind Virtual Functions and


Polymorphism along with suitable programming
examples for each.

Ans –

Virtual Functions

By default, C++ matches a function call with the correct function


definition at compile time. This is called static binding. You can specify
that the compiler match a function call with the correct function
definition at run time; this is called dynamic binding. You declare a
function with the keyword virtual if you want the compiler to use dynamic
binding for that specific function.

The following examples demonstrate the differences between static and


dynamic binding. The first example demonstrates static binding:

#include <iostream>
using namespace std;
struct A {
void f() { cout << "Class A" << endl; }
};
struct B: A {
void f() { cout << "Class B" << endl; }
};
void g(A& arg) {
arg.f();
}
int main() {
B x;
g(x);
}

The following is the output of the above example:

Class A
When function g() is called, function A::f() is called, although the argument
refers to an object of type B. At compile time, the compiler knows only
that the argument of function g() will be a reference to an object derived
from A; it cannot determine whether the argument will be a reference to
an object of type A or type B. However, this can be determined at run
time. The following example is the same as the previous example,
except that A::f() is declared with the virtual keyword:

#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "Class A" << endl; }
};
struct B: A {
void f() { cout << "Class B" << endl; }
};
void g(A& arg) {
arg.f();
}
int main() {
B x;
g(x);
}

The following is the output of the above example:

Class B

The virtual keyword indicates to the compiler that it should choose


the appropriate definition of f() not by the type of reference, but by the
type of object that the reference refers to.

Therefore, a virtual function is a member function you may


redefine for other derived classes, and can ensure that the compiler will
call the redefined virtual function for an object of the corresponding
derived class, even if you call that function with a pointer or reference to
a base class of the object.

A class that declares or inherits a virtual function is called a polymorphic


class.

You redefine a virtual member function, like any member function,


in any derived class. Suppose you declare a virtual function named f in a
class A, and you derive directly or indirectly from A a class named B. If
you declare a function named f in class B with the same name and same
parameter list as A::f, then B::f is also virtual (regardless whether or not
you declare B::f with the virtual keyword) and it overrides A::f. However, if
the parameter lists of A::f and B::f are different, A::f and B::f are considered
different, B::f does not override A::f, and B::f is not virtual (unless you have
declared it with the virtual keyword). Instead B::f hides A::f. The following
example demonstrates this:

#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "Class A" << endl; }
};
struct B: A {
void f(int) { cout << "Class B" << endl; }
};
struct C: B {
void f() { cout << "Class C" << endl; }
};
int main() {
B b; C c;
A* pa1 = &b;
A* pa2 = &c;
// b.f();
pa1->f();
pa2->f();
}

The following is the output of the above example:

Class A

Class C

The function B::f is not virtual. It hides A::f. Thus the compiler will
not allow the function call b.f(). The function C::f is virtual; it overrides A::f
even though A::f is not visible in C.

If you declare a base class destructor as virtual, a derived class


destructor will override that base class destructor, even though
destructors are not inherited.

The return type of an overriding virtual function may differ from


the return type of the overridden virtual function. This overriding
function would then be called a covariant virtual function. Suppose that
B::f overrides the virtual function A::f. The return types of A::f and B::f may
differ if all the following conditions are met:

· The function B::f returns a reference or pointer to a class of type T, and


A::f returns a pointer or a reference to an unambiguous direct or indirect
base class of T.

· The const or volatile qualification of the pointer or reference returned


by B::f has the same or less const or volatile qualification of the pointer or
reference returned by A::f.
· The return type of B::f must be complete at the point of declaration of
B::f, or it can be of type B.

The following example demonstrates this:

#include <iostream>
using namespace std;
struct A { };
class B : private A {
friend class D;
friend class F;
};
A global_A;
B global_B;
struct C {
virtual A* f() {
cout << "A* C::f()" << endl;
return &global_A;
}
};
struct D : C {
B* f() {
cout << "B* D::f()" << endl;
return &global_B;
}
};
struct E;
struct F : C {
// Error:
// E is incomplete
// E* f();
};
struct G : C {
// Error:
// A is an inaccessible base class of B
// B* f();
};
int main() {
D d;
C* cp = &d;
D* dp = &d;
A* ap = cp->f();
B* bp = dp->f();
};

The following is the output of the above example:

B* D::f()

B* D::f()
The statement A* ap = cp->f() calls D::f() and converts the pointer returned
to type A*. The statement B* bp = dp->f() calls D::f() as well but does not
convert the pointer returned; the type returned is B*. The compiler would
not allow the declaration of the virtual function F::f() because E is not a
complete class. The compiler would not allow the declaration of the
virtual function G::f() because class A is not an accessible base class of B
(unlike friend classes D and F, the definition of B does not give access to
its members for class G).

A virtual function cannot be global or static because, by definition, a


virtual function is a member function of a base class and relies on a
specific object to determine which implementation of the function is
called. You can declare a virtual function to be a friend of another class.

If a function is declared virtual in its base class, you can still access it
directly using the scope resolution (::) operator. In this case, the virtual
function call mechanism is suppressed and the function implementation
defined in the base class is used. In addition, if you do not override a
virtual member function in a derived class, a call to that function uses
the function implementation defined in the base class.

A virtual function must be one of the following:

· Defined

· Declared pure

· Defined and declared pure

A base class containing one or more pure virtual member functions is


called an abstract class.

Polymorphism

In simple terms, polymorphism lets you treat derived class members just
like their parent class’s members.

More precisely, polymorphism is the ability of objects belonging to


different types to respond to method calls of methods of the same name,
each one according to an appropriate type-specific behavior. The
programmer (and the program) does not have to know the exact type of
the object in advance, so this behavior can be implemented at run time
(this is called late binding or dynamic binding).

The different objects involved only need to present a compatible


interface to the clients (the calling routines). That is, there must be
public methods with the same name and the same parameter sets in all
the objects. In principle, the object types may be unrelated, but since
they share a common interface, they are often implemented as
subclasses of the same parent class. Though it is not required, it is
understood that the different methods will also produce similar results
(for example, returning values of the same type).

In practical terms, polymorphism means that if class B inherits from class


A, it doesn’t have to inherit everything about class A; it can do some of
the things that class A does differently. This means that the same “verb”
can result in different actions as appropriate for a specific class, so
controlling code can issue the same command to a series of objects and
get appropriately different results from each one.

Polymorphism allows client programs to be written based only on the


abstract interfaces of the objects which will be manipulated (interface
inheritance). This means that future extension in the form of new types
of objects is easy, if the new objects conform to the original interface. In
particular, with object-oriented polymorphism, the original client
program does not even need to be recompiled (only relinked) in order to
make use of new types exhibiting new (but interface-conformant)
behavior.

(In C++, for instance, this is possible because the interface definition for
a class defines a memory layout, the virtual function table describing
where pointers to functions can be found. Future, new classes can work
with old, precompiled code because the new classes must conform to the
abstract class interface, meaning that the layout of the new class’s
virtual function table is the same as before; the old, precompiled code
can still look at the same memory offsets relative to the start of the
object’s memory in order to find a pointer to the new function. It is only
that the new virtual function table points to a new implementation of the
functions in the table, thus allowing new, interface-compliant behavior
with old, precompiled code.)

Consider the following example:

#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Animal {
public:
Animal(const string & name) : name(name) { }
virtual ~Animal() { }
virtual const string talk() = 0;
const string name;
};
class Cat : public Animal {
public:
Cat(const string & name) : Animal(name) { }
virtual ~Cat() { }
virtual const string talk() { return "Meow!"; }
};
class Dog : public Animal {
public:
Dog(const string & name) : Animal(name) { }
virtual ~Dog() { }
virtual const string talk() { return "Arf! Arf!"; }
};
// prints the following:
//
// Missy: Meow!
// Mr. Bojangles: Meow!
// Lassie: Arf! Arf!
int main() {
Animal * animals [] = {
new Cat("Missy"),
new Cat("Mr. Bojangles"),
new Dog("Lassie")
};
for(int i = 0; i < 3; i++)
cout << animals[i]->name << ": " << animals[i]->talk() << endl;
for(int i = 0; i < 3; i++)
delete animals[i];
return 0;
}

Note that the talk() method is explicitly declared as virtual. This is because
polymorphic method calls have relatively high overhead in C++. This
overhead is lessened by treating all method calls as non-polymorphic,
unless explicitly marked as virtual by the developer.

4. Write a program in C++ demonstrating various file


handling techniques.

Ans –

#include<iostream.h>
#include<fstream.h>
#include<conio.h>
#include<stdio.h>
#include<process.h>
#include<string.h>

struct student
{ int rollno;
char name[20];
int marks;
}s;
void main()
{ ifstream fin;
ofstream fout;
int ch,no;
do
{ clrscr();
cout<<"\nMenu\n";
cout<<"\n1. Add";
cout<<"\n2. Search";
cout<<"\n3. Display";
cout<<"\n4. Exit";
cout<<"\n\nEnter your choice: ";cin>>ch;
switch(ch)
{ case 1:
{ cout<<"\nEnter rollno. :";cin>>s.rollno;
cout<<"\nName: ";gets(s.name);
cout<<"\nMarks: ";cin>>s.marks;
fout.open("student.dat",ios::binary|ios::app);
fout.write((char*)&s,sizeof(student));
fout.close();
break;
}
case 2:
{ cout<<"\nEnter rollno. to be searched: ";cin>>no;
fin.open("student.dat",ios::binary|ios::in);
while(!fin.eof())
{ fin.read((char*)&s,sizeof(student));
if (s.rollno==no)
{ cout<<"\nRollno: "<<s.rollno;
cout<<"\nName: "<<s.name;
cout<<"\nMarks: "<<s.marks;
getch();
break;
}
}
fin.close();
break;
}

case 3:
{ fin.open("student.dat",ios::binary|ios::in);
while(!fin.eof())
{ clrscr();
fin.read((char*)&s,sizeof(student));
cout<<"\nRoll No. :"<<s.rollno;
cout<<"\nName: "<<s.name;
cout<<"\nMarks: "<<s.marks;
getch();
}
fin.close(); break;
}

case 4:exit(0);
} while (ch<=4);
}

5. Write about the following with the help of suitable


programming examples:
A) Throwing an Exception B) Catching an
Exception

Ans –

Throwing an Exception

If you encounter an exceptional situation in your code – that is, one


where you don’t have enough information in the current context to
decide what to do – you can send information about the error into a
larger context by creating an object containing that information and
“throwing” it out of your current context. This is called throwing an
exception. Here’s what it looks like:

throw myerror(“something bad happened”);

myerror is an ordinary class, which takes a char* as its argument. You


can use any type when you throw (including built-in types), but often
you’ll use special types created just for throwing exceptions. The
keyword throw causes a number of relatively magical things to happen.
First it creates an object that isn’t there under normal program
execution, and of course the constructor is called for that object. Then
the object is, in effect, “returned” from the function, even though that
object type isn’t normally what the function is designed to return.

A simplistic way to think about exception handling is as an alternate


return mechanism, although you get into trouble if you take the analogy
too far – you can also exit from ordinary scopes by throwing an
exception. But a value is returned, and the function or scope exits. Any
similarity to function returns ends there because where you return to is
someplace completely different than for a normal function call. (You end
up in an appropriate exception handler that may be miles away from
where the exception was thrown.) In addition, only objects that were
successfully created at the time of the exception are destroyed (unlike a
normal function return that assumes all the objects in the scope must be
destroyed). Of course, the exception object itself is also properly cleaned
up at the appropriate point. In addition, you can throw as many different
types of objects as you want. Typically, you’ll throw a different type for
each different type of error. The idea is to store the information in the
object and the type of object, so someone in the bigger context can
figure out what to do with your exception.

Catching an Exception

If a function throws an exception, it must assume that exception is


caught and dealt with. As mentioned before, one of the advantages of
C++ exception handling is that it allows you to concentrate on the
problem you’re actually trying to solve in one place, and then deal with
the errors from that code in another place.

The try block

If you’re inside a function and you throw an exception (or a called


function throws an exception), that function will exit in the process of
throwing. If you don’t want a throw to leave a function, you can set up a
special block within the function where you try to solve your actual
programming problem (and potentially generate exceptions). This is
called the try block because you try your various function calls there. The
try block is an ordinary scope, preceded by the keyword try:

try {

// Code that may generate exceptions

If you were carefully checking for errors without using exception


handling, you’d have to surround every function call with setup and test
code, even if you call the same function several times. With exception
handling, you put everything in a try block without error checking. This
means your code is a lot easier to write and easier to read because the
goal of the code is not confused with the error checking.

Exception handlers

Of course, the thrown exception must end up someplace. This is the


exception handler, and there’s one for every exception type you want to
catch. Exception handlers immediately follow the try block and are
denoted by the keyword catch:
try {
// code that may generate exceptions
} catch(type1 id1) {
// handle exceptions of type1
} catch(type2 id2) {
// handle exceptions of type2
}
// etc…

Each catch clause (exception handler) is like a little function that takes a
single argument of one particular type. The identifier (id1, id2, and so on)
may be used inside the handler, just like a function argument, although
sometimes there is no identifier because it’s not needed in the handler –
the exception type gives you enough information to deal with it.

The handlers must appear directly after the try block. If an exception is
thrown, the exception handling mechanism goes hunting for the first
handler with an argument that matches the type of the exception. Then
it enters that catch clause, and the exception is considered handled.
(The search for handlers stops once the catch clause is finished.) Only
the matching catch clause executes; it’s not like a switch statement
where you need a break after each case to prevent the remaining ones
from executing. Notice that, within the try block, a number of different
function calls might generate the same exception, but you only need one
handler.

6. Write about the following with the help of suitable


programming examples:
A) Uncaught Exceptions B) Standard
Exceptions

Ans –

Uncaught Exceptions

If none of the exception handlers following a particular try block matches


an exception, that exception moves to the next-higher context, that is,
the function or try block surrounding the try block that failed to catch the
exception. (The location of this higher-context try block is not always
obvious at first glance.) This process continues until, at some level, a
handler matches the exception. At that point, the exception is
considered “caught,” and no further searching occurs.

If no handler at any level catches the exception, it is “uncaught” or


“unhandled.” An uncaught exception also occurs if a new exception is
thrown before an existing exception reaches its handler – the most
common reason for this is that the constructor for the exception object
itself causes a new exception.

terminate()

If an exception is uncaught, the special function terminate() is


automatically called. Like unexpected(), terminate is actually a pointer to
a function. Its default value is the Standard C library function abort(),
which immediately exits the program with no calls to the normal
termination functions (which means that destructors for global and static
objects might not be called).

No cleanups occur for an uncaught exception; that is, no destructors are


called. If you don’t wrap your code (including, if necessary, all the code
in main()) in a try block followed by handlers and ending with a default
handler (catch(…)) to catch all exceptions, then you will take your lumps.
An uncaught exception should be thought of as a programming error.

set_terminate()

You can install your own terminate() function using the standard
set_terminate() function, which returns a pointer to the terminate()
function you are replacing, so you can restore it later if you want. Your
custom terminate() must take no arguments and have a void return
value. In addition, any terminate() handler you install must not return or
throw an exception, but instead must call some sort of program-
termination function. If terminate() is called, it means the problem is
unrecoverable. Like unexpected(), the terminate() function pointer
should never be null.

Here’s an example showing the use of set_terminate(). Here, the return


value is saved and restored so the terminate() function can be used to
help isolate the section of code where the uncaught exception is
occurring:

//: C07:Trmnator.cpp

// Use of set_terminate()

// Also shows uncaught exceptions

#include <exception>
#include <iostream>
#include <cstdlib>
using namespace std;
void terminator() {
cout << "I’ll be back!" << endl;
abort();
}
void (*old_terminate)()
= set_terminate(terminator);
class Botch {
public:
class Fruit {};
void f() {
cout << "Botch::f()" << endl;
throw Fruit();
}
~Botch() { throw ‘c’; }
};
int main() {
try{
Botch b;
b.f();
} catch(…) {
cout << "inside catch(…)" << endl;
}
} ///:~

The definition of old_terminate looks a bit confusing at first: It not


only creates a pointer to a function, but it initializes that pointer to the
return value of set_terminate(). Even though you may be familiar with
seeing a semicolon right after a pointer-to-function definition, it’s just
another kind of variable and may be initialized when it is defined.

The class Botch not only throws an exception inside f(), but also in
its destructor. This is one of the situations that causes a call to
terminate(), as you can see in main(). Even though the exception handler
says catch(…), which would seem to catch everything and leave no
cause for terminate() to be called, terminate() is called anyway, because
in the process of cleaning up the objects on the stack to handle one
exception, the Botch destructor is called, and that generates a second
exception, forcing a call to terminate(). Thus, a destructor that throws an
exception or causes one to be thrown is a design error.

Standard Exceptions

The set of exceptions used with the Standard C++ library are also
available for your own use. Generally it’s easier and faster to start with a
standard exception class than to try to define your own. If the standard
class doesn’t do what you need, you can derive from it.

The following tables describe the standard exceptions:

The base class for all the exceptions thrown by the C++
exception standard library. You can ask what() and get a result that
can be displayed as a character representation.
logic_error Derived from exception. Reports program logic errors,
which could presumably be detected before the program
executes.
runtime_erro Derived from exception. Reports runtime errors, which can
r presumably be detected only when the program executes.

The iostream exception class ios::failure is also derived from


exception, but it has no further subclasses.

The classes in both of the following tables can be used as they are,
or they can act as base classes to derive your own more specific types of
exceptions.

Exception classes derived


from logic_error
domain_error Reports violations of a precondition.
Indicates an invalid argument to the function
invalid_argument
it’s thrown from.
Indicates an attempt to produce an object
whose length is greater than or equal to NPOS
length_error
(the largest representable value of type
size_t).
out_of_range Reports an out-of-range argument.
Thrown for executing an invalid dynamic_cast
bad_cast
expression in run-time type identification
Reports a null pointer p in an expression
bad_typeid
typeid(*p).
Exception classes derived from
runtime_error
Reports violation of a
range_error
postcondition.
overflow_error Reports an arithmetic overflow.
Reports a failure to allocate
bad_alloc
storage.

7. Describe the following STL Components:


A) Containers B) Iterators C)
Algorithms

Ans –

A)Containers
B)Iterators

Iteration or loops are important statements in c++ which helps to


accomplish repeatitive execution of programming statements. There are
three loop statements in C++ : while loop, do while loop and for loop

While loop

Syntax: while (condition expression)

{
Statement1;
Statement 2;
}
In the above example, condition expression is evaluated and if the
condition is true, then the statement1 and statement2 is executed.
After execution, the condition is checked again. If true, the
statements inside the while loop are executed again. This continues
until the loop condition becomes false. Hence the variable used in the
loop condition should be modified inside the loop so that the loop
termination condition is reached. Otherwise the loop will be executed
infinitely and the program will hang when executed. Also the loop
variable should be iniatialised to avoid the variable taking junk value
and create bugs in the program.

C) Algorithms

8. Write about the following with respect to:


A) Instance Diagrams
B) Sequence Diagrams
C) Collaboration Diagrams

Ans –

A)Instance Diagram

Interaction diagrams are composed mainly of instances and


messages. An instance is said to be the realization of a class that is if we
have a class Doctor, than the instances are Dr. Jones, Dr. Smith, etc. In
an object oriented application, instances are what exist when you
instantiate a class (create a new variable with the class as its datatype).
In the UML, instances are represented as rectangles with a single label
formatted as:

instanceName: datatype

You can choose to name the instance or not, but the datatype
should always be specified. Below the name, you can also list the
attributes and their values. In Visual Case, you can map attributes from
your class and enter new values specific to that instance. Attributes need
only be shown when they are important and you don’t have to specify
and show all of the attributes of a class.

Messages represent operation calls. That is, if an instance calls an


operation in itself or another class, a message is passed. Also, upon the
completion of the operation a return message is sent back to the
instance that initiated the call.

The format for message labels is:

Sequence Iteration [Guard] : name (parameters)

Sequence represents the order in which the message is called. The


sequence is redundant on sequence diagrams, but required on
collaboration diagrams

Iteration – an asterix (*) is shown to represent iteration if the message is


called repeatedly.

Guard – an optional boolean expression (the result is either true or false)


that determines if the message is called.

Name represents the operation being called.

Parameters represent the parameters on the operation being called.

B)Sequence Diagram

Sequence diagrams emphasize the order in which things happen,


while collaboration diagrams give more flexibility in their layout. You can
use whichever you prefer when drawing interactions, as both show the
same information.

An example sequence diagram for our logon collaboration is shown:


Things to Note:

· The flow of time is shown from top to bottom, that is messages higher
on the diagram happen before those lower down

· The blue boxes are instances of the represented classes, and the
vertical bars below are timelines

· The arrows (links) are messages – operation calls and returns from
operations

· The hide and show messages use guards to determine which to call.
Guards are always shown in square braces [ ] and represent constraints
on the message (the message is sent only if the constraint is satisfied)

· The messages are labeled with the operation being called and
parameters are shown. You can choose to enter the parameters or not –
this is dependent upon their importance to the collaboration being shown

· The sequence numbers are not shown on the messages as the


sequence is intrinsic to the diagram

Asynchronous Messages

You can specify a message as asynchronous if processing can


continue while the message is being executed. In the example below,
the asynchronous call does not block processing for the regular call right
below. This is useful if the operation being called is run remotely, or in
another thread.

C)Collaboration Diagram

Collaborations are more complex to follow than sequence diagrams, but


they do provide the added benefit of more flexibility in terms of spatial
layout.
Above is our logon interaction shown as a collaboration diagram. Notice
that each message is numbered in sequence, because it is not obvious
from the diagram, the order of the messages.

Lollipop Interfaces

Another advantage over the sequence diagram is that collaboration


diagrams allow you to show lollipop interfaces.

Suppose that our DatabaseAccess class implemented an interface called


Queryable. If the logon manager only has access to the interface, we
can show that the message is called through the interface by including a
lollipop interface on the diagram. The stick of the lollipop indicates that
the class DatabaseAccess realizes Queryable.

Das könnte Ihnen auch gefallen