Master of Computer Application (MCA) - Semester II: MC0066 - OOPS Using C++ - 4 Credits
Master of Computer Application (MCA) - Semester II: MC0066 - OOPS Using C++ - 4 Credits
1. Distinguished between procedural language language. And Explain the key features of OOP.
and
OOP
Answer : 1.Procedural language focuses on organizing program statements into procedures or functions. Larger programswere either broken into functions or modules whereas in Object Oriented Programming bundles both data andfunctions into one unit known as object. 2. One of the main problems with Procedural approach for programming was data being completely forgotten. Theempha si s was on the action and the data wa s only used in the entire process. Wherea s in Obj ect Oriented approach overcomes this problem by modeling data and functions together there by allowing only certain functionsto access the required data. 3. The procedural languages had limitations of extensibility as there was limited support for creating user defineddatatypes and defining how these datatypes will be handled whereas in OOP language provides this flexibilitythrough the concept of class
4. Another limitation of the procedural languages is that the program model is not closer to real world objects For example, if you want to develop a gaming application of car race, what data would you use and what functions you would require is difficult questions to answer in a procedural approach. In the object oriented approach solves this further by conceptualizing the problem as group of objects which havetheir own specific data and functionality. In the car game example, we would create several objects such as player,car, traffic signal and so on. Key Features of OOP : Encapsulation : Data and functions are said to be encapsulated in an single entity as object. Data Hiding : The data is said to be hidden thus not allowing accidental modification. Inheritance : Inheritance is one of the most powerful feature of Object Oriented Programming Languages thatallows you to derive a class from an existing class and inherit all the characteristics and behaviour of the parentclass. This feature allows easy modification of existing code and also reuse code. The ability to reuse componentsof a program is an important feature for any programming language. Polymorphism : 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 theprogram) does not have to know the exact type of the object in advance, so this behavior can be implemented atrun time (this is called late binding or dynamic binding). Overloading : Operator overloading feature allows users to define how basic operators work with objects. Theoperator + will be adding two numbers when used with integer variables. However when used with user definedstring class, + operator may concatenate two strings. Similarly same functions with same function name canperform different actions depending upon which object calls the
function. This feature of C++ where same operatorsor functions behave differently depending upon what they are operating on is called as polymorphism (Same thingwith different forms). Operator overloading is a kind of polymorphism. 2. What is function overloading? Write a c++ program to implement a function overloaded Answer : Function overloading is a feature of C++ that allows us to create multiple functions with the same name, so long as they have different parameters. Consider the following function: 1 int Add(int nX, int nY) 2{ 3 4} This trivial function adds two integers. However, what if we also need to add two floating point numbers? This function is not at all suitable, as any floating point parameters would be converted to integers, causing the floating point arguments to lose their fractional values. One way to work around this issue is to define multiple functions with slightly different names: return nX + nY;
1 int AddI(int nX, int nY) 2 { 3 4 } 5 6 double AddD(double dX, double dY) 7 { 8 9 } However, for best effect, this requires that you define a consistent naming standard, remember the name of all the different flavors of the function, and call the correct one (calling AddD() with integer parameters may produce the wrong result due to precision issues). Function overloading provides a better solution. Using function overloading, we can declare another Add() function that takes double parameters: 1 double Add(double dX, double dY) 2 { 3 4 } We now have two version of Add(): 1 int Add(int nX, int nY); // integer version return dX + dY; return dX + dY; return nX + nY;
Which version of Add() gets called depends on the arguments used in the call if we provide two ints, C++ will know we mean to call Add(int, int). If we provide two floating point numbers, C+ + will know we mean to call Add(double, double). In fact, we can define as many overloaded Add() functions as we want, so long as each Add() function has unique parameters. Consequently, its also possible to define Add() functions with a differing number of parameters: 1 int Add(int nX, int nY, int nZ) 2 { 3 4 } Even though this Add() function has 3 parameters instead of 2, because the parameters are different than any other version of Add(), this is valid. Note that the functions return type is NOT considered when overloading functions. Consider the case where you want to write a function that returns a random number, but you need a version that will return an int, and another version that will return a double. You might be tempted to do this: 1 int GetRandomValue(); 2 double GetRandomValue(); But the compiler will flag this as an error. These two functions have the same parameters (none), and consequently, the second GetRandomValue() will be treated as an erroneous redeclaration of the first. Consequently, these functions will need to be given different names. return nX + nY + nZ;
Also keep in mind that declaring a typedef does not introduce a new type consequently, the following the two declarations of Print() are considered identical: 1 typedef char *string; 2 void Print(string szValue); 3 void Print(char *szValue); A function is overloaded when same name is given to different function. However, the two functions with the same name will differ at least in one of the following. a) The number of parameters b) The data type of parameters c) The order of appearance These three together are referred to as the function signature. For example if we have two functions : void foo(int i,char a); void boo(int j,char b); Their signature is the same (int ,char) but a function void moo(int i,int j) ; has a signature (int, int) which is different. While overloading a function, the return type of the functions need to be the same. In general functions are overloaded when : 1. Functions differ in function signature. 2. Return type of the functions is the same. Here s a basic example of function overloading with c++ #include <iostream> using namespace std;
a.calc(6,7); } First the overloaded function in this example is calc. If you have noticed we have in our arith class two functions with the name calc. The fist one takes one integer number as a parameter and prints the square of the number. The second calc function takes two integer numbers as parameters, multiplies the numbers and prints the product. This is all we need for making a successful overloading of a function. a) we have two functions with the same name : calc b) we have different signatures : (int) , (int, int) c) return type is the same : void
3. Discuss the constructors and Destructors with suitable example Answer : Constructors : Constr u ct or s are memb er functi ons of a class which hav e sa me na me as the class name. Constructors are called automatically whenever an object of the class is created. This feature makes it very usefulto initialize the class. data members whenever a new object is created. It also can perform any other function that needs to be performedfor all the objects of the class without explicitly specifying it. Destructors : Destructors on the other hand are also member functions with the same name as class but areprefixed with tilde (~) sign to differentiate it from the constructor. They are invoked automatically whenever the objects life expires or it is destroyed. It can be used toreturn the memory back to the operating system if the memory was dynamically allocated.
The following program implements the constructor and destructors for a Class // constdest.cpp # include<iostream.h> class sample { private: int data; public: sample() { data=0; cout<<Constructor invoked<<endl; } ~sample() { cout<<Destructor invoked; }; void display() { cout<<Data=<<data<<endl;} }; void main()
If you run the above program you will get the output as follows: Constructor invoked Data=0 Destructor invoked When object of sample class, object is created, automatically the constructor is invoked and data is initialized tozero. When the program ends the object is destroyed which invokes the destructor. Please note that both theconstructor and destructor is declared as public and they have no return value. However, constructors can havearguments and can be overloaded so that different constructors can be called depending upon the arguments that ispassed. Destructors on the other hand cannot be overloaded and cannot have any arguments.
4.
What do you mean by operator overloading? Illustrate with suitable example for overloading Unary operators.
Answer : Operator overloading : Operator overloading is an interesting feature of C++ that allows programmers to specifyhow various arithmetic, relational and many other operators work with user defined data-types or classes. Toperform addition of two distance objects we used a call d3.add(d1,d2). Instead of such statements it would be moreclear if we could use statements like d3=d1+d2. This is possible only if we inform compiler about how + operator works with distance class. It helps to use the default operators with the user defined objects for making the code simpler. However there areseveral problems with operator overloading which you should be aware of. When using operator overloading, theoperator should perform only the most obvious function. Otherwise it will lead to more confusion. If you areoverloading + operator for distance class it should add two distance objects, and should not do something else. Several operators such as dot operator, scope resolution (::) operator, conditional operator (?:) etc cannot beoverloaded. Therefore operator overloading should be used for a class where it is required to perform obviousfunctions with the default operators and there can be no other meaning for the same. Overloading Unary Operators We have seen that unary operators are those operators which work on one operator. Some of the unary operatorsare ++, --, and minus (-). Operator overloading works similar to any member function of a class. But it is not invokedusing dot operator. Just like member function the operator has a return value and takes arguments. It is invoked bythe operand which uses it. In case of overloading of unary operators, the calling operand can be either left or right of the operator like in case of increment and decrement operators. While defining the operator functionality for theclass the keyword operator is used. Example : //unary.cpp # include <iostream.h> class counter
{ unsigned int count; public: counter() { count=0; } int getcount() { return count; } void operator ++() { count++; } }; void main() { counter c1; c1++; ++c1; cout<<c1.getcount(); } In the above example, the operator ++ is defined to return void and take no arguments. All unary operators do nottake no arguments as it operates on only one operand and that operand itself invokes the operator. Therefore theoperand is sent by default.
} }; struct B: A { void f() { cout << "Class B" << endl; } }; void g(A& arg) { arg.f(); } int main() { B x; g(x); }
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; }
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. 6. Difference between a static member function and non-static member functions with appropriate example. Answer: A non-static member function is invoked on objects of the class it belongs to. It has implicitly access to the this pointer representing the current object. Through this pointer, it may access other members easily and with full access privileges (i.e. access private members).
A non-member function has no implicit this. In the sample below, bar is a member function whilefreebar is not. Both do more or less the same, but note how bar gets an implicit object pointer viathis (also only bar has privileged access to foo's members, freebar only has access to public members).
class foo { public: void bar() { this->x = 0; // equivalent to x = 0; } int x; }; void freebar(foo* thefoo) { thefoo->x = 1; }
Semantically a member function is more than just a function with an implicit this parameter. It is meant to define the behaviour of an object (i.e. a car object would have drive(), stop() as member functions). there are also static member functions which have full privileges but don't get an implicitthis nor are they invoked through an instance of the class (but rather through the full name of the class).
7. Writer C++ program to demonstrate the complete implementation of class template stack. Answer : A class template definition looks like a regular class definition, except it is prefixed by the keyword template. For example, here is the definition of a class template for a Stack. //stack.h #pragma once template <class T> class Stack { public: Stack(int = 10); ~Stack() { delete [] stackPtr; } int push(const T&); int pop(T&); // pop an element off the stack int isEmpty()const { return top == -1; } int isFull() const { return top == size - 1; } private: int size; // Number of elements on Stack int top; T* stackPtr; }; //constructor with the default size 10 template <class T>
Stack<T>::Stack(int s) { size = s > 0 && s < 1000 ? s : 10; top = -1; // initialize stack stackPtr = new T[size]; } // push an element onto the Stack template <class T> int Stack<T>::push(const T& item) { if (!isFull()) { stackPtr[++top] = item; return 1; // push successful } return 0; // push unsuccessful } // pop an element off the Stack template <class T> int Stack<T>::pop(T& popValue) { if (!isEmpty()) { popValue = stackPtr[top--]; return 1; // pop successful }
7. What is template specialization? Describe a scenario in which template class partial specialization is considered appropriate Answer : In some cases it is possible to override the template-generated code by providing special definitions for specific types. This is called template specialization. The following example defines a template class specialization for template class stream. #include <iostream> using namespace std; template <class T> class stream { public: void f() { cout << "stream<T>::f()"<< endl; } }; template <> class stream<char> { public: void f() { cout << "stream<char>::f()"<< endl; } }; int main() { stream<int> si; stream<char> sc; si.f();
sc.f(); return 0; } Output: stream<T>::f() stream<char>::f() In the above example, stream<char> is used as the definition of streams of chars; other streams will be handled by the template class generated from the class template. Template Class Partial Specialization You may want to generate a specialization of the class for just one parameter, for example //base template class template<typename T1, typename T2> class X { }; //partial specialization template<typename T1> class X<T1, int> { }; int main() { // generates an instantiation from the base template X<char, char> xcc ; //generates an instantiation from the partial specialization X<char, int> xii ; return 0 ; } A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list.
Assignment Set 2
Questions From Book-ID : B0681
implement the multiple inheritances Answer : Deriving directly from more than one class is usually called
multiple inheritance. Since it's widely believed that this concept complicates the design and debuggers can have a hard time with it, multiple inheritance can be a controversial topic. However, multiple inheritance is an important feature in C++ and C++ programmers think of it as a very good structuring tool. let's consider the following real world example: class Animal { //describes the behavior of the animal }
class Drawing { //contains the drawing properties of the entity like colors, size etc }
Suppose we need to create a displayable snake object. We can inherit class Animal in class Drawing or class Drawing in class Animal and then use the derived one as a base class for snake, but neither of the solutions is appropriate because they both create a dependency between two independent concepts. So, instead, we should use multiple inheritance and derive both Drawing and Animal in the snake class: class Snake : public Animal, public Drawing As you may noticed, another solution in the above design could be to use composition instead of inheritance and have animal and drawing as members of class snake : class Snake { public: ... private: Animal *m_animal; Drawing *m_drawing; }
But now the relation between animal and snake is that snake contains an animal instead of snake is an animal, which is conceptually wrong, and the lack of categorization makes the design unrealistic. This will make the code harder to understand and also harder to reuse because we lose the advantage of dynamic binding and polymorphism. The benefit of dynamic binding and polymorphism is that they help making the code easier to extend (make it possible to create operations that work on a class of objects that share the same interface). Suppose we want later to create another animal, a lion for example. The old code that operates on objects of type Animal will work without change, and in the new oneLion class, if it overrides one of the animal methods, dynamic binding ensures that its new methods are going to be executed properly instead of the Animal versions of those methods depending on the object type they are called from.
Using inheritance instead of composition, we can completely hide some members of the parent class, or allow access only for subclasses (classes derived from it) by specifying them as protected. As with simple (single) inheritance the derived class has access to all the non-private members of the base classes. When the Snake's class constructor is executed it first initializes the base classes by calling their appropriate constructors in the order they are defined in appear in the list defining the list of inherited classes when they are declared (first Animal and then Drawing). Note that our example has only two base classes, but C++ doesn't impose any constraint on the number of classes that can be inherited. Some design patterns benefit from the use of multiple inheritance. One of them is the Adapter pattern: it uses multiple inheritance to adapt one interface to another. This pattern is usually used to provide implementation for an abstract class (when you want to use an existing class and its interface is not appropriate, so you need to .glue. another interface with that class). Another design pattern that can be created with multiple inheritance is the Observer pattern. In this design pattern a class, called subject, maintains a list of observers that are notified by some change (user input) by calling one of their functions.
int main() {
int rows2 = 1; int columns2 = 3; float **matrixTwo = (float**)malloc(rows2 * sizeof(*matrixTwo)); for(int i = 0; i < rows2; i++) { matrixTwo[i] = (float*)malloc(columns2 * sizeof(float)); }
float **result; result = (float**)malloc(rows1 * sizeof(*result)); for(int i = 0; i < rows1; i++) { result[i] = (float*)malloc(columns2 * sizeof(float));
matrixMultiply(3,1,matrixOne,1,3,matrixTwo, result);
return 0;
void matrixMultiply(int rows1, int cols1, float **mat1, int rows2, int cols2, float **mat2, float **result) {
} else{
float tempResult;
for(int j=0;j<cols2;j++) {
tempResult = 0;
for(int k=0;k<rows2;k++) {
tempResult += mat1[i][k]*mat2[k][j];
result[i][j] = tempResult;
2.. Discuss the types of Inheritance with suitable example for each. Answer: Inheritance is a very powerful featu re 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. Types Of Inheritance :
Hierarchical Inheritance : We can have several classes derived from a single base class like shown below.
Multi Level 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 : Multiple Inheritance is the process of inheriting a class from more than one parent class. This would be required in several instances where you would like to have the functionalities of several classes . This is also extensively used in class libraries.
The syntax for implementing multiple inheritance is similar to single inheritance. Let us suppose, there are two classes A and B, you want to derive a class C from A and B. The syntax of class definition will be as follows:
class C : public A, public B { }; 2. Write a c++ program to implements the relational operator overloading for the distance class Answer: There are various relational operators supported by C++ language like (<, >, <=, >=, ==, etc.) which can be used to compare C++ built-in data types. You can overload any of these operators, which can be used to compare the objects of a class.
Following example explain how a < operator can be overloaded and similar way you can overload other relational operators.
#include <iostream> using namespace std;
class Distance { private: int feet; int inches; public: // required constructors Distance(){ feet = 0; inches = 0; } Distance(int f, int i){ feet = f; inches = i; } // method to display distance void displayDistance() { cout << "F: " << feet << " I:" << inches <<endl; } // overloaded minus (-) operator // 0 to infinite // 0 to 12
Distance operator- () { feet = -feet; inches = -inches; return Distance(feet, inches); } // overloaded < operator bool operator <(const Distance& d) { if(feet < d.feet) { return true; } if(feet == d.feet && inches < d.inches) { return true; } return false; } }; int main() { Distance D1(11, 10), D2(5, 11);
if( D1 < D2 ) {
cout << "D1 is less than D2 " << endl; } else { cout << "D2 is less than D1 " << endl; } return 0; }
Termination: In termination (which is what C++ supports) you assume the error is so critical theres no way to get back to where the exception occurred. Whoever threw the exception decided there was no way to salvage the situation, and they dont want to come back.
Resumption : It means the exception handler is expected to do something to rectify the situation, and then the faulting function is retried, presuming success the second time. If you want resumption, you still hope to continue execution after the exception is handled, so your exception is more like a function call which is how you should set up situations in C++ where you want resumption-like behavior (that is, dont throw an exception; call a function that fixes the problem). Alternatively, place your try block inside a while loop that keeps reentering the try block until the result is satisfactory.
Historically, programmers using operating systems that supported resumptive exception handling eventually ended up using termination-like code and skipping resumption. So although resumption sounds attractive at first, it seems it isnt quite so useful in practice. One reason may be the distance that can occur between the exception and its handler; its one thing to terminate to a handler thats far away, but to jump to that handler and then back again may be too conceptually difficult for large systems where the exception can be generated from many points
4. Discuss the various STL components in brief Answer : he C++ STL (Standard Template Library) is a powerful set of C++ template classes to provides general-purpose templatized classes and functions that implement many popular and commonly used algorithms and data structures like vectors, lists, queues, and stacks. At the core of the C++ Standard Template Library are following three well-structured components:
Component Containers Description Containers are used to manage collections of objects of a certain kind. There are several different types of containers like deque, list, vector, map etc. Algorithms act on containers. They provide the means by which you will perform initialization, sorting, searching, and transforming of the contents of containers. Iterators are used to step through the elements of collections of objects. These collections may be containers or subsets of containers.
Algorithms
Iterators
We will discuss about all the three C++ STL components in next chapter while discussing C++ Standard Library. For now, keep in mind that all the three components have a rich set of pre-defined functions which help us in doing complicated tasks in very easy fashion. Let us take the following program demonstrates the vector container (a C++ Standard Template) which is similar to an array with an exception that it automatically handles its own storage requirements in case it grows:
// display the original size of vec cout << "vector size = " << vec.size() << endl;
// display extended size of vec cout << "extended vector size = " << vec.size() << endl;
// access 5 values from the vector for(i = 0; i < 5; i++){ cout << "value of vec [" << i << "] = " << vec[i]
<< endl; }
// use iterator to access the values vector<int>::iterator v = vec.begin(); while( v != vec.end()) { cout << "value of v = " << *v << endl; v++; }
return 0; } When the above code is compiled and executed, it produces following result: vector size = 0 extended vector size = 5 value of vec [0] = 0 value of vec [1] = 1 value of vec [2] = 2 value of vec [3] = 3 value of vec [4] = 4 value of v = 0 value of v = 1 value of v = 2 value of v = 3
value of v = 4
6. Describe the time overhead of operations on sequence containers Answer : Sequence Containers : There are three types of sequence containers to
store data in linear sequence. These are the vector, deque and list : vector<Type> deque<Type> list<Type> To choose a container, decide what sort of operations you will most frequently perform on your data, and then use the following table to help in choosing. Time overhead of operations on sequence containers
Operation Access first Element Access last element Access random element Add/delete at beginning Add/delete at end Add/delete at random
Vector : The vector class is similar to an array and is able to access elements at any position with a constant time overhead , O(1). Insertion and deletion at the end of vector is cheap . Memory overhead of a vector is very low and comparable to a normal array. Deque : The double ended queue, has similar properties to a vector, but as the name suggests you can efficiently insert or delete elements at either end. Deque like vector is not very good at inserting or deleting elements at random positions, but it does allow random access to elements using the array like syntax. List : Lists dont provide access like an array or vector, but are suited to applications where you want to add or remove elements to or from the middle. They are implemented as double linked lists structures in order to support bidirectional iterators.
8. Discuss how object diagrams are different from class diagram. Answer : 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.