MC0066 - Set 2

27
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.

Transcript of MC0066 - Set 2

Page 1: MC0066 - Set 2

February 2010Master 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

Page 2: MC0066 - Set 2

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.

Page 3: MC0066 - Set 2

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 listpublic:list() // no-argument default constructor

Page 4: MC0066 - Set 2

{ first=NULL;} // empty list : no first nodevoid additem(int d) // adds new node to the beginning of the list{node* newnode; // node pointernewnode = new node; // create a new nodenewnode->data=d; //assign value newnode->next=first; // assign pointer to the firstfirst=newnode; // first now points to new node}void display() // displays all nodes{ node* temp; // pointer to nodetemp=first;while (temp!=NULL){ cout<< endl<<temp->data; // print datatemp=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();}

Page 5: MC0066 - Set 2

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 {

Page 6: MC0066 - Set 2

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() {

Page 7: MC0066 - Set 2

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;

Page 8: MC0066 - Set 2

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,

Page 9: MC0066 - Set 2

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

Page 10: MC0066 - Set 2

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;}

Page 11: MC0066 - Set 2

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;

Page 12: MC0066 - Set 2

} 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

Page 13: MC0066 - Set 2

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:

Page 14: MC0066 - Set 2

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

Page 15: MC0066 - Set 2

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

Page 16: MC0066 - Set 2

#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:

Page 17: MC0066 - Set 2

exceptionThe base class for all the exceptions thrown by the C++ 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_error Derived from exception. Reports runtime errors, which can 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_errordomain_error Reports violations of a precondition.

invalid_argument Indicates an invalid argument to the function it’s thrown from.

length_errorIndicates an attempt to produce an object whose length is greater than or equal to NPOS (the largest representable value of type size_t).

out_of_range Reports an out-of-range argument.

bad_cast Thrown for executing an invalid dynamic_cast expression in run-time type identification

bad_typeid Reports a null pointer p in an expression typeid(*p).

Exception classes derived from runtime_errorrange_error Reports violation of a postcondition.overflow_error Reports an arithmetic overflow.bad_alloc Reports a failure to allocate storage.

7. Describe the following STL Components: A) Containers B) Iterators C) Algorithms

Ans –

A) Containers

Page 18: MC0066 - Set 2

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 DiagramsB) Sequence DiagramsC) 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

Page 19: MC0066 - Set 2

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:

Page 20: MC0066 - Set 2

· 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

Page 21: MC0066 - Set 2

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.