You are on page 1of 13

1. Write advantage of multiple inheritances. And write ac++ program to implement the multiple inheritances.


Consider writing a simulation of a network of computers. Each node in the network is represented by an object of class Switch, each user or computer by an object of class Terminal, and each communication line by an object of class Line. One way to monitor the simulation (or a real network of the same structure) would be to display the state of objects of various classes on a screen. Each object to be displayed is represented as an object of class Displayed. Objects of class Displayed are under control of a display manager that ensures regular update of a screen and/or data base. The classes Terminal and Switch are derived from a class Task that provides the basic facilities for co-routine style behavior. Objects of class Task are under control of a task manager (scheduler) that manages the real processor(s). Ideally Task and Displayed are classes from a standard library. If you want to display a terminal class Terminal must be derived from class Displayed. Class Terminal, however, is already derived from class Task. In a single inheritance language, such as the original version of C++[4] or Simula67, we have only two ways of solving this problem: deriving Task from Displayed or deriving Displayed from Task. Neither is ideal since they both create a dependency between the library versions of two fundamental and independent concepts. Ideally one would want to be able choose between saying that a Terminal is a Task and a Displayed; that a Line is a Displayed but not a Task; and that a Switch is a Task but not a Displayed. The ability to express this using a class hierarchy, that is, to derive a class from more than one base class, is usually referred to as multiple inheritance. Other examples involve the representation of various kinds of windows in a window system[7] and the representation of various kinds of processors and compilers for a multi-machine, multi-environment debugger[1]. In general, multiple inheritance allows a user to combine independent (and not so independent) concepts represented as classes into a composite concept represented as a derived class. A common way of using multiple inheritance is for a designer to provide sets of base classes with the intention that a user creates new classes by choosing base classes from each of the relevant sets. Thus a programmer creates new concepts using a recipe like pick an A and/or a B. In the window example, a user might specify a new kind of window by selecting a style of window interaction (from the set of interaction base classes) and a style of appearance (from the set of base classes defining display options). In the debugger example, a programmer would specify a debugger by choosing a processor and a compiler. Given multiple inheritance and N concepts each of which might somehow be combined with one of M other concepts, we need N+M classes to represent all the combined concepts. Given only single inheritance, we need to replicate information and provide N+M+N*M classes. Single inheritance handles cases where N==1 or M==1. The usefulness of multiple inheritance for avoiding replication hinges on the importance of examples where the values of N and M are both larger than 1. It appears that examples with N>=2 and M>=2 are not uncommon; the window and debugger examples described above will typically have both N and M larger than 2.
#include<iosteram.h> #include<conio.h> Class M { Protected: Int m; Public : Void get_M();

}; Class N { Protected: Int n; Public: Void get_N(); }; Class p: public M, public N { Public: Void disply(void); }; Void M ::get_m(int x) { m=x; } Void N::get_n(int y) { n=y; } Void P:: disply(void) { Cout<<m=<<m<<endl; Cout<<n=<<n<<endl; Cout<<m*n=<<m*n<<endl; } int main() { P p; p.get_m(10); p.get_n(20); p.display(); return 0; }

2. Discuss the types of Inheritance with suitable example for each. In object-oriented programming, inheritance is a way to form new classes (instances of which are called objects) using classes that have already been defined. The new classes, known as derived classes, take over (or inherit) attributes and behavior of the pre-existing classes, which are referred to as base classes (or ancestor classes). It is intended to help reuse existing code with little or no modification. Inheritance provides the support for representation by Categorization in computer languages. Categorization is a powerful mechanism number of information processing, crucial to human learning by means of generalization (what is known about specific entities is applied to a wider group given a belongs relation can be established) and cognitive economy (less information needs to be stored about each specific entity, only its particularities). Inheritance is also sometimes called generalization, because the is-a relationships represent a hierarchy between classes of objects. For instance, a "fruit" is a generalization of "apple", "orange", "mango" and many others. One can consider fruit to be an abstraction of apple, orange, etc. Conversely, since apples are fruit (i.e., an apple is-a fruit), apples may naturally inherit all the properties common to all fruit, such

as being a fleshy container for the seed of a plant. An advantage of inheritance is that modules with sufficiently similar interfaces can share a lot of code, reducing the complexity of the program. Inheritance therefore has another view, a dual, called polymorphism, which describes many pieces of code being controlled by shared control code. Inheritance is typically accomplished either by overriding (replacing) one or more methods exposed by ancestor, or by adding new methods to those exposed by an ancestor. Types.... 1.Single 2.Multiple 3.Multilevel 4.Hybrid 5.Hierarchical

Inhertance is defined as create a new class with the use the property of existing class objects Types of inheriance: Single Inhertance --------------Class A { } Class B: Public A { }

MultiLevel Inhertance: ---------------class A { } class B:Public A {

} Class C:Public B { } Mulitiple Inhertance: ---------------------class A { } class B:Public A { } Class C:Public B,Public A { } Hierachical Inheritance: -----------------------class A { } class B:Public A { } class c: public A { }

Write a c++ program to implements the relational operator overloading for the distance class.


#include class circle { int r; public : circle () : r(0) circle(int r) {

this->r=r; } int operator < (circle T) { if(r > T.r) return 1; else return 0; } void area() { cout<<Area = <<3.14*r*r; } }; void main() { circle c1(5),c2(10); clrscr(); if(c1 < c2) c1.area(); else c2.area(); getch(); } OUTPUT Area = 314

4. Write the advantages of using exception handling with its basic models.
Use exceptions to: - Fix the problem and call the function (which caused the exception) again. - Patch things up and continue without retrying the function. - Calculate some alternative result instead of what the function was supposed to produce. - Do whatever you can in the current context and rethrow the same exception to a higher context. - Do whatever you can in the current context and throw a different exception to a higher context.

- Terminate the program. - Make your library and program safer. This is a short-term investment (for debugging) and a longterm investment (for application robustness). Always use exception specifications The exception specification is like a function prototype: It tells the user to write exception handling code and what exceptions to handle. It tells the compiler the exceptions that may come out of this function. Of course, you cant always anticipate by looking at the code what exceptions will arise from a particular function. Sometimes the functions it calls produce an unexpected exception, and sometimes an old function that didnt throw an exception is replaced with a new one that does, and youll get a call to unexpected(). Anytime you use exception specifications or call functions that do, you should create your own unexpected() function that logs a message and rethrows the same exception. Start with standard exceptions Check out the Standard C++ library exceptions before creating your own. If a standard exception does what you need, chances are its a lot easier for your user to understand and handle. If the exception type you want isnt part of the standard library, try to derive one from an existing standard exception. Its nice for your users if they can always write their code to expect the what() function defined in the exception() class interface. Nest your own exceptions If you create exceptions for your particular class, its a very good idea to nest the exception classes inside your class to provide a clear message to the reader that this exception is used only for your class. In addition, it prevents the pollution of the namespace. You can nest your exceptions even if youre deriving them from C++ standard exceptions. Use exception hierarchies Exception hierarchies provide a valuable way to classify the different types of critical errors that may be encountered with your class or library. This gives helpful information to users, assists them in organizing their code, and gives them the option of ignoring all the specific types of exceptions and just catching the base-class type. Also, any exceptions added later by inheriting from the same base class will not force all existing code to be rewritten the baseclass handler will catch the new exception. Of course, the Standard C++ exceptions are a good example of an exception hierarchy, and one that you can use to build upon. Catch by reference, not by value If you throw an object of a derived class and it is caught by value in a handler for an object of the base class, that object is sliced that is, the derivedclass elements are cut off and youll end up with the base-class object being passed. Chances are this is not what you want because the object will behave like a base-class object and not the derived class object it really is (or rather, was before it was sliced).

5. Discuss the various STL components in brief. STL Components

The STL is based on the cooperation of different well-structured components, key of which are containers, iterators, and algorithms:

Containers are used to manage collections of objects of a certain kind. Every kind of container has its own advantages and disadvantages, so having different container types reflects different

requirements for collections in programs. The containers may be implemented as arrays or as linked lists, or they may have a special key for every element. Iterators are used to step through the elements of collections of objects. These collections may be containers or subsets of containers. The major advantage of iterators is that they offer a small but common interface for any arbitrary container type. For example, one operation of this interface lets the iterator step to the next element in the collection. This is done independently of the internal structure of the collection. Regardless of whether the collection is an array or a tree, it works. This is because every container class provides its own iterator type that simply "does the right thing" because it knows the internal structure of its container. The interface for iterators is almost the same as for ordinary pointers. To increment an iterator you call operator ++. To access the value of an iterator you use operator *. So, you might consider an iterator a kind of a smart pointer that translates the call "go to the next element" into whatever is appropriate.

Algorithms are used to process the elements of collections. For example, they can search, sort, modify, or simply use the elements for different purposes. Algorithms use iterators. Thus, an algorithm has to be written only once to work with arbitrary containers because the iterator interface for iterators is common for all container types. To give algorithms more flexibility you can supply certain auxiliary functions called by the algorithms. Thus, you can use a general algorithm to suit your needs even if that need is very special or complex. For example, you can provide your own search criterion or a special operation to combine elements.

The concept of the STL is based on a separation of data and operations. The data is managed by container classes, and the operations are defined by configurable algorithms. Iterators are the glue between these two components. They let any algorithm interact with any container STL Components

In a way, the STL concept contradicts the original idea of object-oriented programming: The STL separates data and algorithms rather than combining them. However, the reason for doing so is very important. In principle, you can combine every kind of container with every kind of algorithm, so the result is a very flexible but still rather small framework.

One fundamental aspect of the STL is that all components work with arbitrary types. As the name "standard template library" indicates, all components are templates for any type (provided the type is able to perform the required operations). Thus the STL is a good example of the concept of generic programming. Containers and algorithms are generic for arbitrary types and classes respectively. The STL provides even more generic components. By using certain adapters and function objects (or functors) you can supplement, constrain, or configure the algorithms and the interfaces for special needs. However, I'm jumping the gun. First, I want to explain the concept step-by-step by using examples. This is probably the best way to understand and become familiar with the STL.


Describe the time overhead of operations on sequence containers.

The following sequence containers are predefined in the STL:

Vectors Deques Lists

In addition you can use strings and ordinary arrays as a (kind of) sequence container. Vectors A vector manages its elements in a dynamic array. It enables random access, which means you can access each element directly with the corresponding index. Appending and removing elements at the end of the array is very fast.[2] However, inserting an element in the middle or at the beginning of the array takes time because all the following elements have to be moved to make room for it while maintaining the order. The following example defines a vector for integer values, inserts six elements, and prints the elements of the vector: // stl/vector1.cpp #include <iostream> #include <vector> using namespace std; int main() { vector<int> coll;

//vector container for integer elements

// append elements with values 1 to 6 for (int i=1; i<=6; ++i) { coll.push_back(i);

} //print all elements followed by a space for (int i=0; i<coll.size( ); ++i) { cout << coll[i] << ' '; } cout << endl; } With #include <vector> the header file for vectors is included. The declaration vector<int> coll; creates a vector for elements of type int. The vector is not initialized by any value, so the default constructor creates it as an empty collection. The push_back() function appends an element to the container: coll.push_back(i); This member function is provided for all sequence containers. The size() member function returns the number of elements of a container: for (int i=0; i<coll.size(); ++i) { ... } This function is provided for any container class. By using the subscript operator [], you can access a single element of a vector: cout << coll[i] << ' '; Here the elements are written to the standard output, so the output of the whole program is as follows: 123456 Deques

The term deque (it rhymes with "check" [3]) is an abbreviation for "double-ended queue." It is a dynamic array that is implemented so that it can grow in both directions. Thus, inserting elements at the end and at the beginning is fast. However, inserting elements in the middle takes time because elements must be moved. The following example declares a deque for floating-point values, inserts elements from 1.1 to 6.6 at the front of the container, and prints all elements of the deque: // stl/deque1.cpp #include <iostream> #include <deque> using namespace std; int main() { deque<float> coll;

//deque container for floating-point elements

//insert elements from 1.1 to 6.6 each at the front for (int i=1; i<=6; ++i) { coll.push_front(i*1. 1); //insert at the front } //print all elements followed by a space for (int i=0; i<coll.size(); ++i) { cout << coll[i] << ' '; } cout << endl; } In this example, with #include <deque> the header file for deques is included. The declaration deque<float> coll; creates an empty collection of floating-point values. The push_front() function is used to insert elements: coll.push_front(i*1.1);

push_front() inserts an element at the front of the collection. Note that this kind of insertion results in a reverse order of the elements because each element gets inserted in front of the previous inserted elements. Thus, the output of the program is as follows: 6.6 5.5 4.4 3.3 2.2 1.1 You could also insert elements in a deque by using the push_back() member function. The push_front() function, however, is not provided for vectors because it would have a bad runtime for vectors (if you insert an element at the front of a vector, all elements have to be moved). Usually, the STL containers provide only those special member functions that in general have "good" timing ("good" timing normally means constant or logarithmic complexity). This prevents a programmer from calling a function that might cause bad performance. Lists A list is implemented as a doubly linked list of elements. This means each element in a list has its own segment of memory and refers to its predecessor and its successor. Lists do not provide random access. For example, to access the tenth element, you must navigate the first nine elements by following the chain of their links. However, a step to the next or previous element is possible in constant time. Thus, the general access to an arbitrary element takes linear time (the average distance is proportional to the number of elements). This is a lot worse than the amortized constant time provided by vectors and deques. The advantage of a list is that the insertion or removal of an element is fast at any position. Only the links must be changed. This implies that moving an element in the middle of a list is very fast compared with moving an element in a vector or a deque. The following example creates an empty list of characters, inserts all characters from 'a' to 'z', and prints all elements by using a loop that actually prints and removes the first element of the collection: // stl/list1.cpp #include <iostream> #include <list> using namespace std; int main() { list<char> coll; //list container for character elements // append elements from 'a' to 'z' for (char c='a'; c<= ' z '; ++c) { coll.push_back(c); } /* print all elements * - while there are elements * - print and remove the first element

*/ while (! coll.empty()) { cout << coll.front() << ' '; coll.pop_front(); } cout << endl; } As usual, the header file for lists, <list>, is used to define a collection of type list for character values: list<char> coll; The empty() member function returns whether the container has no elements. The loop continues as long as it returns true (that is, the container contains elements): while (! coll.empty()) { ... } Inside the loop, the front() member function returns the actual first element: cout << coll.front() << ' '; The pop_front() function removes the first element: coll.pop_front(); Note that pop_front() does not return the element it removed. Thus, you can't combine the previous two statements into one. The output of the program depends on the actual character set. For the ASCII character set, it is as follows [4]: abcdefghijklmnopqrstuvwxyz Of course it is very strange to "print" the elements of a list by a loop that outputs and removes the actual first element. Usually, you would iterate over all elements. However, direct element access by using operator [] is not provided for lists. This is because lists don't provide random access, and thus using operator [] would cause bad performance. There is another way to loop over the elements and print them by using iterators.

7. Discuss

how object diagrams are different from class diagram.

A class diagram is a graph of Classifier elements connected by their various static relationships. Note that a class diagram may also contain interfaces, packages, relationships, and even instances, such as objects and links. Perhaps a better name would be static structural diagram, but class diagram is shorter and well established. An object diagram is a graph of instances, including objects and data values. A static object diagram is an instance of a class diagram; it shows a snapshot of the detailed state of a system at a point in time. The use of object diagrams is fairly limited, mainly to show examples of data structures. Tools need not support a separate format for object diagrams. Class diagrams can contain objects, so a class diagram with objects and no classes is an object diagram. The phrase is useful, however, to characterize a particular usage achievable in various ways. The basic idea is that class diagrams focus on classes and object diagrams focus on objects, but it is possible to mix classes and objects on the same diagram for various purposes, so the separation is not rigid. Class diagrams show the logical, static structure of a system and are central the Unified Modeling Language. Object diagrams play a smaller role in UML. They are used primarily for documenting test cases and scenarios, and for discussing examples.