Oops
Oops
Programming
Unit 1: Review of Object-
oriented Programming
1. Outline the essential steps involved in carrying out a
procedure-oriented programming study.
Answer: Programming practices have evolved considerably over the past few
decades. As more and more programmers gained experience problems unknown
hitherto, began to surface. The programming community became ever more
concerned about the philosophy that they adopt in programming and approaches
they practice in program development.
Factors like productivity, reliability, cost effectiveness, reusability etc. started to
become major concern. A lot of conscious efforts were put to understand these
problems and to seek possible solutions. This is precisely the reason why more
and more programming languages have been developed and still continue to
develop. In addition to this, approaches to program development have also been
under intense research thereby evolving different frame works. One such, and
probably the most popular one is object-oriented programming approach or
simply OOP.
For example, an manager class is a type of the class employee, which again is a
type of the class person as illustrated below.
The principle behind this sort of division is that each derived class shares
common characteristics with the class from which it is derived. The power of
inheritance lies in the fact that all common features of the subclasses can be
accumulated in the super class. In other words, a feature is placed in the higher
level of abstraction. Once this is done, such features can be inherited from the
parent class and used in the subclass directly. This
implies that if there are many abstract class definitions available, when a new
class is needed, it is possible that the new class is a specialization of one or
more of the existing classes.
In OOP, the concept of inheritance provides the idea of reusability. This means
that we can add additional features to an existing class without modifying it.
This is possible by deriving a new class from the existing one. The new class
will have the combined features of both the classes. Each subclass defines only
those features that are unique to it. In OOP, The concept of inheritance provides
the idea of reusability. This means that we can add additional features to an
existing class without modifying it. This is possible by deriving
a new class from the existing one. The new class will have the combined
features of both the classes.
1. Through inheritance, we can eliminate redundant code and extend the use of
existing classes.
2. We can build programs from the standard working modules that
communicate with one another, rather than having to start writing the code from
scratch. This leads to saving of development time and higher productivity.
3. The principle of data hiding help the programmer to build secure programs
that cannot be invaded by code in other parts of the program.
4. It is possible to have multiple instances of an object to co-exist without any
interference.
5. It is possible to map objects in the problem domain to those objects in the
program.
6. It is easy partition the work in a project based on objects.
7. The data-centered design approach enables us to capture more details of a
model in implementable form.
8. Object oriented systems can be easily upgraded from small to large systems.
9. Message passing techniques for communication between objects makes the
interface descriptions with external systems much simpler.
10. Software complexity can be easily managed.
4. How are classes different from objects? Illustrate with suitable
examples.
Answer:
Classes: A class represents a set of related objects. The object has some
attributes, whose value consist much of the state of an object. The class of an
object defines what attributes an object has. The entire set of data and code of
an object can be made a user-defined data type with the help of a class.
Classes are user defined data types that behave like the built-in types of a
programming language. Classes have an interface that defines which part of an
object of a class can be accessed from outside and how. A class body that
implements the operations in the interface, and the instance variables that
contain the state of an object of that class.
The data and the operation of a class can be declared as one of the three
types:
(a) Public: These are declarations that are accessible from outside the class to
anyone who can access an object of this class.
(b) Protected: These are declarations that are accessible from outside the class
to anyone who can access an object of this class.
(c) Private: These are declarations that are accessible only from within the class
itself.
Study
The genesis of any software begins with the study of the problems, which it
intends to solve. Software cannot be envisaged unless there is a problem that it
must solve. Therefore, studying the problem in depth, understanding the true
nature of the problem and representing the problem in comprehensible manner
is what necessitates inclusion of this phase.
Analysis
It is a detailed study of the various operations performed by the proposed
software. A key question that is considered in this phase of development is –
What must be done to solve the problem? One aspect of analysis is defining the
boundaries or interface of the software.
During analysis, data are collected in available files, decision points, and
transactions handled by the present system. Bias in data collection and
interpretation can be fatal to the developmental efforts.
Once analysis is completed the analyst has a firm understanding of what is to be
done. The next step is to decide how the problem might be solved. Thus, in the
software systems design, we move from the logical to the physical aspects of
the life cycle.
Design
The most creative and challenging phase of software life cycle is design. The
term design describes both a final software system and a process by which it is
developed. It refers to the technical specifications (analogous to the engineer’s
blueprints) that will be applied in implementing the software system. It also
includes testing the software. The key question around which this phase
revolves is – How should the problem be solved?
The first step is to determine how the output is to be produced and in what
format. Samples of the output (and input) are outlined.
Second, input data and master files (data base) have to be designed to generate
the required output. The operational (processing) phase are handled through
program construction and testing, including a list of the programs needed to
meet the software objectives and complete documentation.
Finally, details related to justification of the system and an estimate of the
impact of the software on the user are documented and evaluated before it is
implemented.
Implementation
This phase is primarily concerned with coding the software design into an
appropriate programming language; testing the programs and installing the
software. User training, site preparation, and data migration are other important
issues in this phase.
Maintenance
Change is inevitable. Software serving the users’ needs in the past may become
less useful or sometimes useless in the changed environment. Users’ priorities,
changes in organizational
requirements, or environmental factors may call for software enhancements. A
bank, for instance, may decided to increase its service charges for checking
accounts from ` 3.00 to ` 450 for a minimum balance of ` 3,000. In this phase
the software is continuously evaluated and modified to suit the changes as they
occur.
Answer:
10. Compare what you look for in the problem description when
applying object-oriented approach in contrast to the procedural
approach. Illustrate with some practical examples.
Answer: Object-oriented approach offers perhaps the most logical description
of the real world. Therefore, it can be applied in almost all the situations of
problem solving. Because of its effectiveness in problem solving and
programming, OOP has been adopted all over the software industry. The
existing systems are being migrated to OOP.
One important aspect of OOP that is particularly beneficial is the framework,
which encompasses all the phases of a system development. Thus, OOA (Object
Oriented Analysis), OOD (Object Oriented Design), OOT (Object Oriented
Testing) etc. are far more suitable tools than their non-object-oriented
counterparts. Above all, the reusability feature of the Object Oriented approach
has facilitated the rapid growth of software both in variety and scope.
Unit 2: Beginning of OOP Language
Review of Tokens
As we know, the smallest individual units in a program are known as tokens. C++ has the
following tokens:
1. Keywords
2. Identifiers
3. Constants
4. Strings
5. Operators
Keywords
The keywords implement specific C++ language features. They are explicitly reserved
identifiers and cannot be used as names for the program variables or other user-defined
program elements.
Identifiers
Identifiers refer to the names of variables, functions, arrays, classes, etc., created by the
programmer. They are the fundamental requirement of any language. Each language has its
own rules for naming these identifiers.
Operators
An operator is a symbol that tells the compiler to perform specific mathematical or logical
manipulations. C++ is rich in built-in operators and provide the following types of operators
−
Arithmetic Operators
Relational Operators
Logical Operators
Bitwise Operators
Assignment Operators
x = 5;
This statement assigns the integer value 5 to the variable x. The assignment operation always
takes place from right to left, and never the other way around:
x = y;
This statement assigns to variable x the value contained in variable y. The value of x at the
moment this statement is executed is lost and replaced by the value of y.
Arithmetic operators ( +, -, *, /, % )
The five arithmetical operations supported by C++ are:
operator description
+ addition
- subtraction
* multiplication
/ division
% modulo
x = 11 % 3;
Compound assignment (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=)
Compound assignment operators modify the current value of a variable by performing an
operation on it. They are equivalent to assigning the result of an operation to the first
operand:
y += x; y = y + x;
x -= 5; x = x - 5;
x /= y; x = x / y;
1 ++x;
2 x+=1;
3 x=x+1;
are all equivalent in its functionality; the three of them increase by one the value of x.
Relational and comparison operators ( ==, !=, >, <, >=, <= )
Two expressions can be compared using relational and equality operators. For example, to
know if two values are equal or if one is greater than the other.
The result of such an operation is either true or false (i.e., a Boolean value).
operator description
== Equal to
!= Not equal to
1 (7 == 5) // evaluates to false
2 (5 > 4) // evaluates to true
3 (3 != 2) // evaluates to true
4 (6 >= 6) // evaluates to true
5 (5 < 5) // evaluates to false
If condition is true, the entire expression evaluates to result1, and otherwise to result2.
Comma operator ( , )
The comma operator (,) is used to separate two or more expressions that are included where
only one expression is expected. When the set of expressions has to be evaluated for a value,
only the right-most expression is considered.
a = (b=3, b+2);
would first assign the value 3 to b, and then assign b+2 to variable a. So, at the end, variable a
would contain the value 5 while variable b would contain value 3.
Bitwise operators ( &, |, ^, ~, <<, >> )
Bitwise operators modify variables considering the bit patterns that represent the values they
store.
| OR Bitwise inclusive OR
sizeof
This operator accepts one parameter, which can be either a type or a variable, and returns the
size in bytes of that type or object:
x = sizeof (char);
Here, x is assigned the value 1, because char is a type with a size of one byte.
Control Structures:
if (condition) statement
Where condition is the expression that is being evaluated. If this condition is true, statement is
executed. If it is false, statement is ignored (not executed) and the program continues right after
this conditional structure.
For example, the following code fragment prints x is 100 only if the value stored in the x variable
is indeed 100:
1 if (x == 100)
2 cout << "x is 100";
If we want more than a single statement to be executed in case that the condition is true we can
specify a block using braces { }:
1 if (x == 100)
2{
3 cout << "x is ";
4 cout << x;
5}
We can additionally specify what we want to happen if the condition is not fulfilled by using the
keyword else. Its form used in conjunction with if is:
For example:
1 if (x == 100)
2 cout << "x is 100";
3 else
4 cout << "x is not 100";
Loops have as purpose to repeat a statement a certain number of times or while a condition is
fulfilled.
and its functionality is simply to repeat statement while the condition set in expression is true.
For example, we are going to make a program to countdown using a while-loop:
Its functionality is exactly the same as the while loop, except that condition in the do-while
loop is evaluated after the execution of statement instead of before, granting at least one
execution of statement even if condition is never fulfilled. For example, the following
example program echoes any number you enter until you enter 0.
1 // number echoer
2
3 #include <iostream>
4 using namespace std;
5
Enter number (0 to end): 12345
6 int main ()
You entered: 12345
7 {
Enter number (0 to end): 160277
8 unsigned long n;
You entered: 160277
9 do {
Enter number (0 to end): 0
10 cout << "Enter number (0 to end): ";
You entered: 0
11 cin >> n;
12 cout << "You entered: " << n << "\n";
13 } while (n != 0);
14 return 0;
15 }
The for loop
and its main function is to repeat statement while condition remains true, like the while loop.
But in addition, the for loop provides specific locations to contain an initialization statement
and an increase statement. So this loop is specially designed to perform a repetitive action
with a counter which is initialized and increased on each iteration.
Arrays can be used to store multiple homogenous data but there are serious
drawbacks of using arrays.
Programmer should allocate the memory of an array when they declare it but most of
time, the exact memory needed cannot be determined until runtime.
The best thing to do in this situation is to declare the array with maximum possible
memory required (declare array with maximum possible size expected) but this
wastes memory.
So, To avoid wastage of memory, you can dynamically allocate the memory required
during runtime using new and delete operator.
What are memory management operators?
There are two types of memory management operators in C++:
new
delete
These two memory management operators are used for allocating and freeing memory blocks
in efficient and convenient ways.
New operator:
The new operator in C++ is used for dynamic storage allocation. This operator can be
used to create object of any type.
In the above statement, new is a keyword and the pointer variable is a variable of type
datatype.
For example:
1. int *a = new int;
2. *a = 20;
or
3. int *a = new int(20);
In the above example, the new operator allocates sufficient memory to hold the object
of datatype int and returns a pointer to its starting point.
the pointer variable a holds the address of memory space allocated.
delete operator:
The delete operator in C++ is used for releasing memory space when the object is no
longer needed.
Once a new operator is used, it is efficient to use the corresponding delete operator for
release of memory.
void main()
{
//Allocates using new operator memory space in memory for storing a integer datatype
int *a= new int;
*a=100;
cout << " The Output is:a= " << *a;
//Memory Released using delete operator
delete a;
}
Output:
Prototype of a function is the function without its body. The C++ compiler
needs to about a
function before you call it, and you can let the compiler know about a function
is two ways – by defining it before you call it or by specifying the function
prototypes before you call it. Many programmers prefer a ‘Top-Down’
approach in which main() appears ahead the user-defined function definition. In
such approach, function access will precede function definition. To overcome
this, we use the function prototypes or we declare the function.
The code snippet given below will cause compilation error because the main
function does not know about the called function one().
void main()
{
…
one(); //incorrect!! No function prototype available for one()
…
}
void one()
{
//function definition
}
To resolve this problem you should define the function before it is called as
shown below:
void one ()
{
//function definition
}
void main()
{
…
one(); //Correct!! Function one is defined before calling
…
}
Another elegant alternative to this problem is to include a function prototype
before it called no matter where the function has been actually defined as shown
below:
Now the next instruction in the main program will get executed. Jumping back
and forth keeping track of where to jump means that there is an overhead in
elapsed time to functions. When the function is declared inline the compiler
replaces the function call with the corresponding function code. As a result the
program does not have to spend time to jump to another location to execute the
function and then come back. But there is a memory overhead. If the function is
called n times in the program then it is copied n times making the program code
or in particular executable code large. If the value of n is large, there is a lot of
memory overhead.
There is not any significance difference between void main () and int main ()
meaning wise but syntactically when you use int main () then you have to return
some integer value at end (as return type is int) while using void main () there
is no such need (as return type is void).
Thus in both cases above it is like normal function except it is called by system.
Coming to int main (int argc, char *argv[]), this syntax for main function is
used when we want to pass parameters to main function (called command line
arguments).
In the above signature of main, first parameter (which must be int) contains no
of arguments and second parameter (which must be string array) contains the
argument values (which are strings). The first argument is always the program
name and subsequent are actual parameters.
#include <iostream>
using namespace std;
int main ()
{
float kel, cel;
cout << "\n\n Convert temperature in Celsius to Kelvin :\n";
cout << " \n";
cout << " Input the temperature in Celsius : ";
cin >> cel;
kel = cel + 273.15;
cout << " The temperature in Celsius : " << cel << endl;
cout << " The temperature in Kelvin : " << kel << endl;
cout << endl;
return 0;
}
Sample Output:
Prototype of a function is the function without its body. The C++ compiler
needs to about a function before you call it, and you can let the compiler know
about a function is two ways – by defining it before you call it or by specifying
the function prototypes before you call it. Many programmers prefer a ‘Top-
Down’ approach in which main() appears ahead the user-defined function
definition. In such approach, function access will precede function definition.
To overcome this, we use the function prototypes or we declare the function.
Function-prototype are usually written at the beginning of a program, ahead of
any programmer-defined functions including main (). In general, function
prototype is written as:
Return-type name (type 1 arg. 1, type 2 arg. 2, ….. type n arg. n);
Where return-type represents the data type of the value returned by the function,
name represents the function name, type 1, type 2 …. type n represents the data-
type of arguments art. 1, arg. 2 … arg n. The function prototypes resemble first
line to function definition, though it ends with the semicolon. Within the
function declaration, the names of arguments are optional but data-types are
necessary for eq. Int count (int);
6. How can inline functions help with the tradeoff of safety vs.
speed?
Answer:
C++ provides facility of inline function, which is designed to speed up the
program execution. This is done by the C++ compiler, which incorporates it into
a program.
Now the next instruction in the main program will get executed. Jumping back
and forth keeping track of where to jump means that there is an overhead in
elapsed time to functions. When the function is declared inline the compiler
replaces the function call with the corresponding function code. As a result the
program does not have to spend time to jump to another location to execute the
function and then come back. But there is a memory overhead. If the function is
called n times in the program then it is copied n times making the program code
or in particular executable code large. If the value of n is large, there is a lot of
memory overhead.
Unit 4: Classes and Objects
1. Define class. What is the difference between structures and
classes in C++?
Answer: Specifying a Class
A class is a way to bind the data and its associated functions together. It allows
the data (and functions) to be hidden, if necessary, from external use. When
defining a class, we are creating a new abstract data-type that can be treated like
any other built-in data-type.
The class declaration describes the type and scope of its members. The class
function definitions describe how the class functions are implemented. The
general form of a class declaration is:
class <class_name>
{
private:
variables declaration;
function declarations;
public:
variable declaration;
function declarations;
};
A) Class
1. A class in C++ can be defined as a collection of related variables and
functions encapsulated in a single structure.
2. A class in C++ is just an extension of a structure used in the C language.
It is a user defined data type. It actually binds the data and its related
functions in one unit.
3. Keyword for the declaration: Class
4. Default access specifier: Private
5. Purpose: Data abstraction and further inheritance
6. Type: Reference
7. Usage: Generally used for large amounts of data.
8. Its object is created on the heap memory.
9. The member variable of class can be initialized directly.
10. It can have all the types of constructor and destructor.
B) Structure
1. A structure in C++ can be referred to as an user defined data type
possessing its own operations.
2. A structure and a class in C language differs a lot as a structure has
limited functionality and features as compared to a class.
3. Keyword for the declaration: Struct
4. Default access specifier: Public
5. Purpose: Generally, grouping of data
6. Type: Value
7. Usage: Generally used for smaller amounts of data.
8. Its object is created on the stack memory.
9. The member variable of structure cannot be initialized directly.
10. It can have only parameterized constructor.
Here's an example (for info on extern "C", see the previous two FAQs).
Fred.h:
#ifdef cplusplus
class Fred {
public:
Fred();
void wilma(int);
private:
int a_;
};
#else
typedef
struct Fred
Fred;
#endif
#ifdef cplusplus
extern "C" {
#endif
#ifdef cplusplus
}
#endif
#endif /*FRED_H*/
Fred.cpp:
// This is C++ code
#include "Fred.h"
Fred::Fred() : a_(0) { }
void Fred::wilma(int a) { }
#include "Fred.h"
int main()
{
Fred fred;
c_function(&fred);
...
}
c-function.c:
/* This is C code */
#include "Fred.h"
The point is simple: your C compiler will not know how to do that pointer
conversion, so the conversion from Derived* to Base*, for example, must take
place in code compiled with a C++ compiler, not in code compiled with a C
compiler.
NOTE: you must be especially careful when converting both to void* since that
conversion will not allow either the C or C++ compiler to do the proper pointer
adjustments! The comparison (x == y) might be false even if (b == d) is
true:
void* x = b;
void* y = d;
if (x == y) { BAD FORM! DO NOT DO THIS!
...
}
}
As stated above, the above pointer conversions will typically happen with
multiple and/or virtual inheritance, but please do not look at that as an
exhaustive list of the only times when the pointer conversions will happen.
You have been warned.
If you really want to use void* pointers, here is the safe way to do it:
void f(Base* b, Derived* d)
{
void* x = b;
void* y = static_cast<Base*>(d); ← If conversion is needed, it
will happen in the static_cast<>
if (x == y) { Validly compares a Base* to a Derived*
...
}
}
Structure is also a user defined data type with a certain template. It is generally
used for grouping of logically related data items. After the creation of
a structure, the variables pertaining to the type of structure can be defined and
used. A structure is used to represent a record. In C++, a structure can have both
data members and functions as classes. Many people find it difficult to
differenciate between a class and a structure. Technically they both are regarded
as the same in C++.
Class Structure
A class in C++ can be A structure can be
defined as a collection of referred to as a user
Definition related variables and defined data type
functions encapsulated in possessing its own
a single structure. operations.
Keyword for the
Class Struct
declaration
Default access specifier Private Public
class myclass struct myclass
{ {
private: private:
data(data_) data(data_)
{} {}
{} {}
}; };
Data abstraction and Generally, grouping of
Purpose
further inheritance data
Type Reference Value
Generally used for large Generally used for
Usage
amounts of data. smaller amounts of data.
4. How does C++ help with the tradeoff of safety vs. usability?
Answer:
With C++ classes, you can have many instances of the data (many objects) and
encapsulation (non-public members).
FQA: This is wildly wrong, and the chances that the FAQ author didn't know it
are extremely low. That's because you can't use FILE* from <stdio.h> or
HWND from <windows.h> or in fact any widely used and/or decent C library
without noticing that the FAQ's claim is wrong.
When you need multiple instances and encapsulation in C, you use a forward
declaration of a struct in the header file, and define it in the implementation file.
That's actually better encapsulation than C++ classes - there's still no run-time
encapsulation (memory can be accidentally/maliciously overwritten), but at
least there's compile-time encapsulation (you don't have to recompile the code
using the interface when you change the implementation).
The fact that a crude C technique for approximating classes is better than the
support for classes built into the C++ language is really shameful. Apparently so
shameful that the FAQ had to distort the facts in an attempt to save face (or else
the readers would wonder whether there's any point to C++ classes at all). The
FQA hereby declares that it will not go down this path. Therefore, we have to
mention this: the forward declaration basically makes it impossible for the
calling code to reserve space for the object at compile time. This means that a
struct declared in a header file or a C++ class can sometimes be allocated more
efficiently than a forward-declared struct. However, this is really about a
different tradeoff - safety vs. efficiency, and there's no escape from this tradeoff.
Either the caller knows about the details such as the size of an object at compile
time - which breaks compile-time encapsulation - or it doesn't, so it can't handle
the allocation.
}
You should get the following output from this program.
The present value of count is 1
The present value of count is 2
The present value of count is 3
The present value of count is 4
The present value of count is 5
struct batting
{
char batsman[20];
int run;
boolean out;
int tot_run;
int overs;
int total_over;
int extra;
};
struct bowling
{
char bowler[20];
int overs;
int maiden_overs;
int runs;
int wicket;
};
void main()
{
batting bat_team[12];
bowling bo_team[12];
cout<<"\n Enter records of batting team :\n\n";
for(int i = o; i < 12 ; i++)
{
cout<<"\n Enter bat's man name :";
gets(bat_team[i].batsman);
cout<<"\n Enter run scored :";
cin>>bat_team[i].run;
cout<<"\n Type 0 NOT-OUT , Type 1 if OUT :";
cin>>bat_team[i].out;
cout<<"\n Enter total run :";
cin>>bat_team[i].total_run;
cout<<"\n Enter no. of overs played :";
cin>>bat_team[i].overs;
cout<<"\n Enter total over:";
cin>>bat_team[i].total_over;
cout<<"\n Extra ?";
cin>>bat_team[i].extra;
}
do
{
clrscr();
cin>>n;
switch(n)
case 1:
if(bat_team.out)
cout<<"OUT";
else
cout<<"NOT-OUT";
cout<< " " << bat_team[i].total_run<< " " << bat_team[i].overs << " " <<
bat_team[i].total_over<< " "<< bat_team[i].extra;
}
break;
case 2:
{
cout<< "\n"<< bo_team[i].bowler<< "\t"<< bo_team[i].overs<< " ";
cout<< " "<< bo_team[i].maiden_overs<< " "<< bo_team[i].runs<< " "<<
bo_team[i].wicket<<" ";
}
break;
default:
cin>>ch;
}while(ch=='y' | | ch == 'Y');
getch();
}
#include <iostream>
using namespace std;
class Calculator
{
protected:
int num1;
int num2;
};
class ChildCalculator: public Calculator
{
public:
int add()
num1 = 2;
num2 = 3;
int sum = num1 + num2;
return sum;
}
};
int main()
{
ChildCalculator myChildObj;
int result = myChildObj.add();
cout << result;
return 0;
}
The base class has just two properties and no method; these properties are
protected. The derived class has one method and no property. Inside the derived
class, the protected properties of the base class are used as identifiers.
Generally, when a derived class is using a member of a base class, it is a method
of the derived class that is using the member, as in this example. The above
code is OK.
# include <iostream.h>
# include <conio.h>
class student
private:
int rn;
float fees;
void read()
rn=12;
fees=145.10;
public:
void show ()
read();
}
};
void main ( )
clrscr ( );
student st;
st.show ( );
getch();
}
Unit 5: Static Members
1. Why did C++ add the class keyword?
You use the class keyword to declare new types. A class is a collection of class
member data, which are variables of various types, including other classes. The
class also contains class functions—or methods—which are functions used to
manipulate the data in the class and to perform other services for the class. You
define objects of the new type in much the same way in which you define any
variable. State the type (class) and then the variable name (the object). You
access the class members and functions by using the dot (.) operator. You use
access control keywords to declare sections of the class as public or private. The
default for access control is private. Each keyword changes the access control
from that point on to the end of the class or until the next access control
keyword. Class declarations end with a closing brace and a semicolon.
2. The const keyword specifies that the values of the variable will
not change throughout the program. Analyze.
Answer:
The keyword, const (for constant), precedes the data type of a variable.
Syntax
const <data type> <name of variable> = <value>;
Example:
const float g = 9.8;
cout << “The acceleration due to gravity is” << g << “m/s2”;
g=g+4; // ERROR!! Const variables cannot be modified.
The const keyword specifies that the values of the variable will not change
throughout the
program. This prevents the programmer from changing the value of variable,
like the one in the example given above. If the keyword, const, has been used
while defining the variable, the compiler will report an error if the programmer
tries to modify it.
3. “The static keyword can be used to declare variables, functions,
class data members and class functions”. Justify with an example.
Answer:
Static member:
The static keyword can be used to declare variables, functions, class data
members and class functions.
By default, an object or variable that is defined outside all blocks has static
duration and external linkage. Static duration means that the object or variable
is allocated when the program starts and is deallocated when the program ends.
External linkage means that the name of the variable is visible from outside the
file in which the variable is declared. Conversely, internal linkage means that
the name is not visible outside the file in which the variable is declared.
•When we declare a variable in a function, the static keyword specifies that the
variable retains its state between calls to that function.
You often need to split a class in half when the two halves will have different
numbers of instances or different lifetimes. In these cases, the two halves
usually need direct access to each other (the two halves used to be in the same
class, so you haven't increased the amount of code that needs direct access to a
data structure; you've simply reshuffled the code into two classes instead of
one). The safest way to implement this is to make the two halves friends of each
other.
If you use friends like just described, you'll keep private things private. People
who don't understand this often make naive efforts to avoid using friendship in
situations like the above, and often they actually destroy encapsulation. They
either use public data (grotesque!), or they make the data accessible between the
halves via public get() and set() member functions. Having a public get() and
set() member function for a private datum is OK only when the private datum
"makes sense" from outside the class (from a user's perspective). In many cases,
these get()/set() member functions are almost as bad as public data: they hide
(only) the name of the private datum, but they don't hide the existence of the
private datum.
Let us look at a sample program given below to access the private data of a
class by non-member functions through friend function. The program declares a
private class example representing variable x and function to input the value for
the same. The friend function to display the value of x takes an object as
argument with the help of which private variable x can be accessed.
#include <iostream.h>
#include <conio.h>
class example
{
private:
int x; .
public:
void getdata ()
{
cout << “Enter the value ofx”<< “\n”;
cin >> x;
}
friend void disp(example);
};
void disp(example eg)
{
cout << “Display the entered number”<< eg.x<< “\n”;
}
main ()
{
example egl;
eg1.getdata();
disp(eg1);
getche();
}
// friend_functions_and_nested_classes.cpp
#include <string.h>
char *rgszMessage[255];
class BufferedIO
{
public:
class BufferedInput
{
public:
friend int GetExtendedErrorStatus();
static char *message;
int iMsgNo;
};
};
char *BufferedIO::BufferedInput::message;
int GetExtendedErrorStatus()
{
int iMsgNo = 1; // assign arbitrary value as message number
strcpy( BufferedIO::BufferedInput::message,
rgszMessage[iMsgNo] );
return iMsgNo;
}
int main()
{
}
The scope of a friend class name is the first nonclass enclosing scope. For
example:
class A {
class B { // arbitrary nested class definitions
friend class C;
};
};
is equivalent to:
class C;
class A {
class B { // arbitrary nested class definitions
friend class C;
};
};
Unit 6: Constructors and Destructors
#include<iostream>
using namespace std;
class prime
{
int a,k,i;
public:
prime(int x)
{
a=x;
}
void calculate()
{
k=1;
{
for(i=2;i<=a/2;i++)
if(a%i==0)
{
k=0;
break;
}
else
{
k=1;
}
}
}
void show()
{
if(k==1)
cout<< "\nThe Number is prime Number.\n";
else
cout<<"\nThe Number is Not prime.\n";
}
};
int main()
{
int a;
cout<<"\nEnter any Number: ";
cin>>a;
prime obj(a);
obj.calculate();
obj.show();
return 0;
}
Output:
Enter any Number: 1231
Process returned 0
A big difference!
Suppose that List is the name of some class. Then function f() declares a local
List object called x:
void f()
{
List x; // Local object named x (of class List)
...
}
But function g() declares a function called x() that returns a List:
void g()
{
List x(); // Function named x (that returns a List)
...
}
Foo::Foo(char x)
{
...
Foo(x, 0); // this line does NOT help initialize the this
object!!
...
}
You can sometimes combine two constructors via a default parameter:
class Foo {
public:
Foo(char x, int y=0); // this line combines the two constructors
...
};
If that doesn't work, e.g., if there isn't an appropriate default parameter that
combines the two constructors, sometimes you can share their common code in
a private init() member function:
class Foo {
public:
Foo(char x);
Foo(char x, int y);
...
private:
void init(char x, int y);
};
Foo::Foo(char x)
{
init(x, int(x) + 7);
...
}
Foo::Foo(char x, int y)
{
init(x, y);
...
}
As if that wasn't bad enough, there's another source of inefficiency when using
assignment in a constructor: the member object will get fully constructed by its
default constructor, and this might, for example, allocate some default amount
of memory or open some default file. All this work could be for naught if the
whatever expression and/or assignment operator causes the object to close that
file and/or release that memory (e.g., if the default constructor didn't allocate a
large enough pool of memory or if it opened the wrong file).
Conclusion: All other things being equal, your code will run faster if you use
initialization lists rather than assignment.
Now for the exceptions. Every rule has exceptions (hmmm; does "every rule has
exceptions" have exceptions? reminds me of Gödel's Incompleteness
Theorems), and there are a couple of exceptions to the "use initialization lists"
rule. Bottom line is to use common sense: if it's cheaper, better, faster, etc. to
not use them, then by all means, don't use them. This might happen when your
class has two constructors that need to initialize the this object's data members
in different orders. Or it might happen when two data members are self-
referential. Or when a data-member needs a reference to the this object, and you
want to avoid a compiler warning about using the this keyword prior to the {
that begins the constructor's body (when your particular compiler happens to
issue that particular warning). Or when you need to do an if/throw test on a
variable (parameter, global, etc.) prior to using that variable to initialize one of
your this members. This list is not exhaustive; please don't write me asking me
to add another "Or when...". The point is simply this: use common sense.
5. What is the “Named Constructor Idiom”?
The problem is that constructors always have the same name as the class.
Therefore the only way to differentiate between the various constructors of a
class is by the parameter list. But if there are lots of constructors, the differences
between them become somewhat subtle and error prone.
With the Named Constructor Idiom, you declare all the class's constructors in
the private or protected sections, and you provide public static methods
that return an object. These static methods are the so-called "Named
Constructors." In general there is one such static method for each different
way to construct an object.
For example, suppose we are building a Point class that represents a position on
the X-Y plane. Turns out there are two common ways to specify a 2-space
coordinate: rectangular coordinates (X+Y), polar coordinates (Radius+Angle).
(Don't worry if you can't remember these; the point isn't the particulars of
coordinate systems; the point is that there are several ways to create a Point
object.) Unfortunately the parameters for these two coordinate systems are the
same: two floats. This would create an ambiguity error in the overloaded
constructors:
class Point {
public:
Point(float x, float y); // Rectangular coordinates
Point(float r, float a); // Polar coordinates (radius and
angle)
// ERROR: Overload is Ambiguous: Point::Point(float,float)
};
int main()
{
Point p = Point(5.7, 1.2); // Ambiguous: Which coordinate
system?
...
}
One way to solve this ambiguity is to use the Named Constructor Idiom:
#include <cmath> // To get std::sin() and std::cos()
class Point {
public:
static Point rectangular(float x, float y); // Rectangular
coord's
static Point polar(float radius, float angle); // Polar
coordinates
// These static methods are the so-called "named constructors"
...
private:
Point(float x, float y); // Rectangular coordinates
float x_, y_;
};
Answer:
When your code returns a local variable by value, your compiler might optimize
away the local variable completely - zero space-cost and zero time-cost - the
local variable never actually exists as a distinct object from the caller's target
variable (see below for specifics about exactly what this means). Other
compilers do not optimize it away.
These are some (!) of the compilers that optimize away the local variable
completely:
These are some (!) of the compilers that do not optimize away the local
variable:
Foo rbv()
{
Foo y = Foo(42, 73);
y.some_method ();
do_something_with(y);
return y;
}
void caller()
{
Foo x = rbv();
...
}
The question addressed in this FAQ is this: How many Foo objects actually get created in the
runtime system? Conceptually there could be as many as three distinct objects: the temporary
created by Foo(42, 73), variable y (in rbv()), and variable x (in caller()). However as we saw
earlier most compilers merge Foo(42, 73) and variable y into the same object, reducing the
total number of objects from 3 to 2. But this FAQ pushes it one step further: does y (in rbv())
show up as a distinct, runtime object from x (in caller())?
Some compilers, including but not limited to those listed above, completely
optimize away local variable y. In those compilers, there is only one Foo object
in the above code: caller()'s variable x is exactly identically the same object as
rbv()'s variable y.
They do this the same way as described earlier: the return-by-value in function
rbv() is implemented as pass-by-pointer, where the pointer points to the
location where the returned object is to be initialized.
void caller()
{
struct Foo x; ← Note: x is not initialized here!
rbv(&x); ← Original C++ code: Foo x = rbv();
...
}
Caveat: this optimization can be applied only when all a function's return
statements return the same local variable. If one return statement in rbv() returned
local variable y but another returned something else, such as a global or a
temporary, the compiler could not alias the local variable into the caller's
destination, x. Verifying that all the function's return statements return the same
local variable requires extra work on the part of the compiler writers, which is
usually why some compilers fail to implement that return-local-by-value
optimization.
7. Why are classes with static data members getting linker errors?
Answer:
Because static data members must be explicitly defined in exactly one
compilation unit. If you didn't do this, you'll probably get an "undefined
external" linker error. For example:
// Fred.h
class Fred {
public:
...
private:
static int j_; // Declares static data member Fred::j_
...
};
The linker will holler at you ("Fred::j_ is not defined") unless you define (as
opposed to merely declare) Fred::j_ in (exactly) one of your source files:
// Fred.cpp
#include "Fred.h"
// Alternatively, if you wish to use the implicit 0 value for static ints:
// int Fred::j_;
The usual place to define static data members of class Fred is file Fred.cpp (or
Fred.C or whatever source file extension you use).
Copy Constructor
A copy constructor method allows an object to be initialized with another object
of the same class. It implies that the values stored in data members of an
existing object can be copied into the data variables of the object being
constructed, provided the objects belong to the same class.
A copy constructor has a single parameter of reference type that refers to the
class itself as shown below:
abc::abc(abc & a)
{
x=a.x;
y=a.y;
}
#include <iostream.h>
#include <conio.h>
class interest
{ int principal, rate, year;
float amount;
public
interest (int p, int n, int r = 10);
void cal (void);
};
interest::interest (int p, int n, int r = 10)
{ principal = p; year = n; rate = r;
};
void interest::cal (void)
{
cout<< “Principal” <<principal;
cout << “\ Rate” <<rate;
cout<< “\ Year” <<year;
amount = (float) (p*n*r)/100;
cout<< “\Amount” <<amount;
};
main ( )
{
interest i1(1000,2);
interest i2(1000, 2,15);
clrscr( );
il.cal();
i2.cal();
}
interest i1(1000,2);
interest i2(1000,2, 15);
The data members principal and year of object i1 are initialized to 1000 and 2
respectively at the time when object i1 is created. The data member rate takes
the default value 10 whereas when the object 12 is created, principal, year and
rate are initialized to 1000,2 and 15 respectively. It is necessary to distinguish
between the default
constructor::construct();
and default argument constructor
construct::construct(int = 0)
The default argument constructor can be called with one or no arguments. When
it is invoked
with no arguments it becomes a default constructor. But when both these forms
are used in a
class, it causes ambiguity for a declaration like construct C1;
The ambiguity is whether to invoke construct: : construct ( ) or construct: :
construct (int=O)
Unit 7: Operator Overloading
Answer:
Operator Purpose
?: Conditional Operator
# Preprocessor Directive
= Assignment operator
0 Subscripting operator
#include <iostream.h>
#include <conio.h>
class integer
{
int x,y,z;
public:
void getdata(int a, int b, int c);
void disp(void);
void operator- (); // overload unary operator minus
};
void integer::getdata(int a, int b, int c)
{
x=a; y=b; z=c;
}
void integer::disp(void)
{
cout << x << “ “;
cout<< y<<“ “;
cout<< z<< “\n”;
}
void integer::operator- () // Defining operator- ()
{
x = -x; y = -y; z = -z;
}
void main ()
{
integer S;
S.getdata(11,-21,-31);
Cout<< “S: “;
S.disp();
-S;
cout<<“-S : “;
S.disp();
getch();
}
You should see the following output.
S: 11 -21 -31
-S:-11 21 31
#include <iostream.h>
#include <conio.h>
class integer
{
private:
int val;
public:
integer();
integer(int one );
integer operator+ (integer objb);
void disp();
};
integer::integer()
{
val = 0;
}
integer::integer(int one)
{
val = one;
}
integer integer::operator+ (integer objb)
{
integer objsum;
objsum.val = val + objb.val;
return (objsum);
}
void integer::disp()
{
cout<< “value =”<< val<< endl;
}
void main()
{
integer obj1(11);
integer obj2(22);
integer objsum;
objsum = objl + obj2;
obj1.disp ();
obj2.disp ();
objsum.disp();
getch();
}
You should see the following output.
value = 11
value = 22
value = 3
The signature of the declaration of a unary operator includes the operator token
and the type of parameter; it does not require the return type and the name of the
parameter.
All the C# unary operators have predefined implementation that will be used by
default in an expression. These unary operators can be overloaded in user-
defined types with custom implementation by defining static member functions
using the "operator" keyword.
class integer
{
intx, y;
public:
int operator + ();
}
int integer: : operator + ( )
{
return (x-y);
}
3. Unary operators, overloaded by means of a member function, take no explicit
argument and return no explicit values. But, those overloaded by means of a
friend function take one reference argument (the object of the relevant class).
4. Binary operators overloaded through a member function take one explicit
argument and those which are overloaded through a friend function take two
explicit arguments.
5. Overloaded operators must either be a non-static class member function or a
global function.
A global function that needs access to private or protected class members must
be declared as a friend of that class. A global function must take at least one
argument that is of class or enumerated type or that is a reference to a class or
enumerated type.
Unit 8: Type Conversion
1. “In a mixed expression constants and variables are of different
data types.” Justify this statement with an example.
Answer: Type Conversions
In a mixed expression constants and variables are of different data types. The
assignment operations cause automatic type conversion between the operand as
per certain rules. The type of data to the right of an assignment operator is
automatically converted to the data type of variable on the left.
This converts float variable y to an integer before its value assigned to x. The
type conversion is automatic as far as data types involved are built in types. We
can also use the assignment operator in case of objects to copy values of all data
members of right hand object to the object on left hand. The objects in this case
are of same data type.
The constructor functions do not support conversion from a class to basic type.
C++ allows us to define a overloaded casting operator that convert a class type
data to basic type. The general form of an overloaded casting operator function,
also referred to as a conversion function, is:
operator typename ()
{
//Program statement
}
This function converts a class type data to type name. For example, the operator
double () converts a class object to type double, in the following conversion
function:
vector:: operator double()
{
double sum = 0;
for(int I = 0; i<size; i++)
sum = sum + v[i] * v[i ]; //scalar magnitude
return sqrt(sum);
}
In a mixed expression constants and variables are of different data types. The
assignment operations cause automatic type conversion between the operand as
per certain rules.
The type of data to the right of an assignment operator is automatically
converted to the data type of variable on the left.
Consider the following example:
int x;
float y = 20.123;
x=y;
This converts float variable y to an integer before its value assigned to x. The
type conversion is automatic as far as data types involved are built in types. We
can also use the assignment operator in case of objects to copy values of all data
members of right hand object to the object on left hand. The objects in this case
are of same data type.
1 short a=2000;
2 int b;
3 b=a;
Here, the value of a has been promoted from short to int and we have not had to
specify any type-casting operator. This is known as a standard conversion.
Standard conversions affect fundamental data types, and allow conversions such
as the conversions between numerical types (short to int, int to float, double to
int...), to or from bool, and some pointer conversions. Some of these
conversions may imply a loss of precision, which the compiler can signal with a
warning. This warning can be avoided with an explicit conversion.
1 class A {};
2 class B { public: B (A a) {} };
3
4 A a;
5 B b=a;
This function converts a class type data to type name. For example, the operator
double() converts a class object to type double, in the following conversion
function:
vector:: operator double()
{
double sum = 0;
for (int I = 0; i<size; i++)
sum = sum + v[i] * v[i ]; //scalar magnitude
return sqrt(sum);
}
Example:
Objl = Obj2 ; //Obj1 and Obj2 are objects of different classes.
Obj1 is an object of class one and Obj2 is an object of class two. The class two
type data is converted to class one type data and the converted value is assigned
to the Obj1. Since the conversion takes place from class two to class one, two is
known as the source and one is known as the destination class.
Such conversion between objects of different classes can be carried out by
either a constructor or a conversion function. Which form to use, depends upon
where we want the type-conversion function to be located, whether in the
source class or in the destination class.
We studied that the casting operator function Operator typename()
Converts the class object of which it is a member to typename. The type name
may be a built-in type or a user defined one (another class type). In the case of
conversions between objects, typename refers to the destination class.
Therefore, when a class needs to be converted, a casting operator function can
be used. The conversion takes place in the source class and the result is given to
the destination class object.
Let us consider a single-argument constructor function which serves as an
instruction for converting the argument’s type to the class type of which it is a
member. The argument belongs to the source class and is passed to the
destination class for conversion. Therefore the conversion constructor must be
placed in the destination class.
#include "iostream.h"
#include "conio.h"
#include "iomanip.h"
class Time
{
int hrs,min;
public:
Time (int ,int); // constructor
operator int(); // casting operator function
~Time() // destructor
{
cout<<"Destructor called..."<<endl;
}
};
void main()
{
clrscr();
int h,m,duration;
cout<<"Enter Hours ";
cin>>h;
cout<<"Enter Minutes ";
cin>>m;
Time t(h,m); // construct object
duration = t; // casting conversion OR duration = (int)t
cout<<"Total Minutes are "<<duration;
cout<<"2nd method operator overloading "<<endl;
duration = t.operator int();
cout<<"Total Minutes are "<<duration;
getch();
}
Unit 9: Inheritance
1. Consider a situation where three kinds of inheritance are
involved. Explain this situation with an example.
Answer: Hybrid Inheritance
2. Public: On the other hand, when the base class is publicly inherited, ‘public
members’ of the base class become ‘public members’ of derived class and
therefore they are accessible to the objects of the derived class.
This type of public inheritance is sometimes called the ” is “, as the parent class
and the child class are of the same type and that could be seen with conversions,
where the child class has more to it, than parent one.
This means that private inheritance is suitable for situations in which we need to
prevent main program to accidentally access some of the methods inherited
from the parent class.
The protected inheritance is less restrictive than private and it is useful when we
need some of the implementations from grandparent class.
.
As we can see from the above table, private inheritance is the most restrictive
and the protected is somehow in between the private and the public type.
# cat p1.cpp
#include <iostream>
class Parent{
public:
void parentMethod ( void ){ cout<<"Inside parent method"<<endl;}
};
# g++ p1.cpp
# ./a.out
Inside child method
Inside parent method
The following example explains how the protected inheritance could be used in
the program.
# cat p2.cpp
#include <iostream>
class GrandParent{
public:
void grandParentMethod ( void ){ cout<<"Method in the grand parent
class"<<endl; }
};
int
main( void ){
Child C;
C.childMethod();
return 0;
We created three levels of inheritance with classes: the grand parent, the
parent and child.
From this chain we have one method at the each of the classes.
The main function has one object of the Child type, afterwards we call the
method childMethod (), which has two calls of parentMethod () and
grandParentMethod ().
# g++ p2.cpp
# ./a.out
Method in the child class
Method in the parent class
Method in the grand parent class
From these two examples, we have learned how this type of inheritance is
implemented.
Now, I need to say that in this case, we have used protected inheritance in the
parent class and if we have used the private inheritance in the second layer of
chain inheritance, we would’ve end up with an error message.
In order to make the most of this two types of inheritance, I will show you how
you could use particular method from parent class if we overload it with some
name method in the child class.
That task should be achieved in the following three ways:
Parent::parentMethod();
using Parent::Method();
Object.Parent::parentMethod ().
In another words, if the child class hides some of the methods from the parent
class, we will have already mentioned methodologies to access hidden methods.
Where the visibility refers to the access specifiers i.e. public, private or
protected. Following program shows the multiple inheritance.
# include < iostream.h>
# include < conio.h>
class father //Declaration of base class1
{
int age;
char name [20];
public:
void get ( );
void show ( );
};
void father: : get ( )
{
cout << “your father name please”;
cin >> name;
cout << “Enter the age”; Notes
cin >> age;
}
void father : : show ( )
{
cout<< “In my father’s name is:”<<name<< “In my father’s age
is: <<age;
}
class mother //Declaration of base class 2
{
char name [20];
int age;
public:
void get ( )
{
cout << “mother’s name please” << “In”;
cin >> name;
cout << “mother’s age please” << “in”;
cin >> age;
}
void show ( )
{
cout << “In my mother’s name is: “<<name;
cout << “In my mother’s age is: “<<age;
class daughter : public father, public mother //derived class:
inheriting
{ //publicly
char name [20]; //the features of both the base class
int std;
public:
void get ( );
void show ( );
};
void daughter :: get ( )
{
father:: get ( );
mother:: get ( );
cout << “child’s name: “;
cin >> name;
cout << “child’s standard”;
cin >> std;
}
void daughter:: show ( )
{
father:: show ( );
nfather :: show ( );
cout << “In child’s name is : “<<name;
cout << “In child’s standard: “<<std;
}
main ( )
{
clrscr ( );
daughter d1;
d1.get ( );
d1.show ( );
}
class g_parent
{
//Body
};
class parent1: virtual public g_parent
{
// Body
};
class parent2: public virtual g_parent
{
// Body
};
class child : public parent1, public parent2
{
// body
};
When a class is virtual base class, C++ takes necessary care to see that only one
copy of that class is inherited, regardless of how many inheritance paths exists
between virtual base class and derived class.
Virtual functions ensure that the correct function is called for an object,
regardless of the expression used to make the function call.
Suppose a base class contains a function declared as virtual and a derived class
defines the same function. The function from the derived class is invoked for
objects of the derived class, even if it is called using a pointer or reference to the
base class. The following example shows a base class that provides an
implementation of the PrintBalance function and two derived classes
Answer: Non-virtual member functions are resolved statically. That is, the
member function is selected statically (at compile-time) based on the type of the
pointer (or reference) to the object.
The compiler creates a v-table for each class that has at least one virtual
function. For example, if class Circle has virtual functions for draw() and
move() and resize(), there would be exactly one v-table associated with class
Circle, even if there were a gazillion Circle objects, and the v-pointer of each of
those Circle objects would point to the Circle v-table. The v-table itself has
pointers to each of the virtual functions in the class. For example, the Circle v-
table would have three pointers: a pointer to Circle::draw (), a pointer to
Circle::move (), and a pointer to Circle::resize ().
During a dispatch of a virtual function, the run-time system follows the object's
v-pointer to the class's v-table, then follows the appropriate slot in the v-table to
the method code.
The space-cost overhead of the above technique is nominal: an extra pointer per
object (but only for objects that will need to do dynamic binding), plus an extra
pointer per method (but only for virtual methods). The time-cost overhead is
also fairly nominal: compared to a normal function call, a virtual function call
requires two extra fetches (one to get the value of the v-pointer, a second to get
the address of the method). None of this runtime activity happens with non-
virtual functions, since the compiler resolves non-virtual functions exclusively
at compile-time based on the type of the pointer.
3. What happens in the hardware when we call a virtual
function? How many layers of indirection are there? How much
overhead isthere?
Answer: Let's work an example. Suppose class Base has 5 virtual functions:
virt0 () through virt4 ().
class Base {
public:
virtual arbitrary_return_type virt0(...arbitrary params...);
virtual arbitrary_return_type virt1(...arbitrary params...);
virtual arbitrary_return_type virt2(...arbitrary params...);
virtual arbitrary_return_type virt3(...arbitrary params...);
virtual arbitrary_return_type virt4(...arbitrary params...);
...
};
Step #1: the compiler builds a static table containing 5 function-pointers,
burying that table into static memory somewhere. Many (not all) compilers
define this table while compiling the .cpp that defines Base's first non-inline
virtual function. We call that table the v-table; let's pretend its technical name is
Base:: vtable. If a function pointer fits into one machine word on the target
hardware platform, Base:: vtable will end up consuming 5 hidden words of
memory. Not 5 per instance, not 5 per function; just 5. It might look something
like the following pseudo-code:
// Pseudo-code (not C++, not C) for a static table defined within file Base.cpp
Base::Base(...arbitrary params...)
: vptr(&Base:: vtable[0]) ← supplied by the compiler, hidden from the
programmer
...
{
...
}
Now let's work out a derived class. Suppose your C++ code defines class Der
that inherits from class Base. The compiler repeats steps #1 and #3 (but not #2).
In step #1, the compiler creates a hidden v-table, keeping the same function-
pointers as in Base:: vtable but replacing those slots that correspond to
overrides. For instance, if Der overrides virt0() through virt2() and inherits the
others as-is, Der's v-table might look something like this (pretend Der doesn't
add any new virtuals):
// Pseudo-code (not C++, not C) for a static table defined within file Der.cpp
Finally, let's see how the compiler implements a call to a virtual function. Your
code might look like this:
// Your original C++ code
void mycode(Base* p)
{
p- >virt3();
}
The compiler has no idea whether this is going to call Base::virt3() or
Der::virt3() or perhaps the virt3() method of another derived class that doesn't
even exist yet. It only knows for sure that you are calling virt3 () which happens
to be the function in slot #3 of the v-table. It rewrites that call into something
like this:
void mycode(Base* p)
{
p-> vptr[3](p);
}
On typical hardware, the machine-code is two 'load's plus a call:
1. The first load gets the v-pointer, storing it into a register, say r1.
2. The second load gets the word at r1 + 3*4 (pretend function-pointers are
4-bytes long, so r1+12 is the pointer to the right class's virt3() function).
Pretend it puts that word into register r2 (or r1 for that matter).
3. The third instruction calls the code at location r2.
class Polygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
};
int main () {
Rectangle rect;
Triangle trgl;
Polygon * ppoly1 = ▭
Polygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << rect.area() << '\n';
cout << trgl.area() << '\n';
return 0;
}
class Line {
public:
void setLength ( double len );
double getLength( void );
Line(); // This is the constructor
private:
double length;
};
return 0;
}
When the above code is compiled and executed, it produces the following result
−
Object is being created
Length of line: 6
A pure virtual function has no definition in the base class. Syntax for declaring
a pure virtual function is as follows:
virtualret_typefun_name(parameter_list)=0;
If the derived class is not providing the definition of pure virtual function then a
compile time error will be generated.
A class that contains one or more than one pure virtual function is called as
abstract class. We cannot create the object of abstract class.
Example:
#include< iostream>
using namespace std;
classbaseClass
{
public:
virtual void show()=0;
};
classderiveClass: public baseClass
{
public:
void show()
{
cout << "Welcome";
}
};
int main() {
deriveClassobj;
obj.show();
return 0;
}
Unit 11: Pointers and Dynamic Memory
Management
1. How does pointer variable differ from simple variable?
Answer:
Normal variables carry the specified data where as the
pointer variable carried the address of the specified data,,,,,
eg: if we give int x =10; ptr p=*x;here x is the normal
variable carries 10 and pointer variable is p which carried
address of the integer variable x.
OR
OR
Answer: Use both the typedef and the #define macro described earlier, and
you're 90% done.
class Fred {
public:
int f(char x, float y);
int g(char x, float y);
int h(char x, float y);
int i(char x, float y);
...
};
#define CALL_MEMBER_FN(object,ptrToMember)
((object).*(ptrToMember))
Now your array of pointers-to-member-functions is straightforward:
int main()
{
int data[5], i;
printf ("Enter elements: ");
return 0;
}
Output
Enter elements: 1
2
3
5
4
You entered:
1
2
3
5
4
1. #include<iostream>
2. #include<stdio.h>
3. using namespace std;
4. main() {
5. char str1[50],str2[50];
6. int str_cmp(char*,char*);
7. cout<<“Enter first string:”;
8. gets(str1);
9. cout<<“Enter second string:”;
10. gets(str2);
11. if(str_cmp(str1,str2))
12. cout<<“nStrings are equal”; else
13. cout<<“nStrings are not equal”;
14. return 0;
15.}
16.int str_cmp(char *s1,char *s2) {
17. while(*s1==*s2) {
18. if(*s1==’’||*s2==’’)
19. break;
20. s1++;
21. s2++;
22. }
23. if(*s1==’’&&*s2==’’)
24. return 1;
25. return 0;
26.}
Unit 12: Console I/O
1. Why should we use <iostream> instead of the traditional
<cstdio>?
Answer: Increase type safety, reduce errors, allow extensibility, and provide
inheritability.
printf() is arguably not broken, and scanf() is perhaps livable despite being error
prone, however both are limited with respect to what C++ I/O can do. C++ I/O
(using << and >>) is, relative to C (using printf() and scanf()):
More type-safe: With <iostream>, the type of object being I/O'd is known
statically by the compiler. In contrast, <cstdio> uses "%" fields to figure
out the types dynamically.
Less error prone: With <iostream>, there are no redundant "%" tokens
that have to be consistent with the actual objects being I/O'd. Removing
redundancy removes a class of errors.
Extensible: The C++ <iostream> mechanism allows new user-defined
types to be I/O'd without breaking existing code. Imagine the chaos if
everyone was simultaneously adding new incompatible "%" fields to
printf() and scanf()?!
Inheritable: The C++ <iostream> mechanism is built from real classes
such as std::ostream and std::istream. Unlike <cstdio>'s FILE*, these are
real classes and hence inheritable. This means you can have other user-
defined things that look and act like streams, yet that do whatever strange
and wonderful things you want. You automatically get to use the zillions
of lines of I/O code written by users you don't even know, and they don't
need to know about your "extended stream" class.
Answer:
Use std: cin.clear () and std:: cin.ignore().
#include <iostream>
#include <limits>
int main()
{
int age = 0;
std::cout << "You are " << age << " years old\n";
...
}
Of course you can also print the error message when the input is out of range.
For example, if you wanted the age to be between 1 and 200, you could change
the while loop to:
...
while ((std::cout << "How old are you? ")
&& (!(std::cin >> age) || age < 1 || age > 200)) {
std::cout << "That's not a number between 1 and 200; ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
...
Here's a sample run:
How old are you? foo
That's not a number between 1 and 200; How old are you? bar
That's not a number between 1 and 200; How old are you? -3
That's not a number between 1 and 200; How old are you? 0
That's not a number between 1 and 200; How old are you? 201
That's not a number between 1 and 200; How old are you? 2
You are 2 years old
Answer: Because the eof state may not get set until after a read is attempted
past the end of file. That is, reading the last byte from a file might not set the eof
state. E.g., suppose the input stream is mapped to a keyboard — in that case it's
not even theoretically possible for the C++ library to predict whether or not the
character that the user just typed will be the last character.
For example, the following code might have an off-by-one error with the count
i:
int i = 0;
while (! std::cin.eof()) { // WRONG! (not reliable)
std::cin >> x;
++i;
// Work with x ...
}
What you really need is:
int i = 0;
while (std::cin >> x) { // RIGHT! (reliable)
++i;
// Work with x ...
}
Answer: sing std::endl flushes the output buffer after sending a '\n', which
means std::endl is more expensive in performance. Obviously if you need to
flush the buffer after sending a '\n', then use std::endl; but if you don't need to
flush the buffer, the code will run faster if you use '\n'.
void f()
{
std::cout << ...stuff... << '\n';
}
This code outputs a '\n', then flushes the output buffer:
void g()
{
std::cout << ...stuff... << std::endl;
}
This code simply flushes the output buffer:
void h()
{
std::cout << ...stuff... << std::flush;
}