Anda di halaman 1dari 53

1

C Structures revisited
A structure is a convenient tool for handling group of logically related data items.
Once a structure type has been defined we can create variables of that type.
Eg:-
struct student
{
char name[20];
int rollno;
float marks;
};
The keyword struct declares student as a new data type that can hold three fields of
different data types.
These fields are known as structure members or elements.
The identifier student is referred to as structure tag or structure name.
It can be used to create variables of type student.
struct student A;
A is a variable of type student and has three member variables as defined by the
template.
Member variables can be accessed using the dot or period operator.
strcpy(A.name, John);
A.Rollno = 999;
A.totalmarks = 595.5;
Limitations of structures
The standard C does not allow the struct data type to be treated like built-in types.
Consider the following structure
struct complex
2

{
float x;
float y;
};
struct complex c1,c2,c3;
The complex numbers c1, c2, c3 can be easily assigned values using dot operator, but we
cannot add two complex numbers or subtract one from the other.
c3 = c1+c2;
is illegal in C.
Another limitation is they do not permit data hiding.
Structure members can be accessed by structure variables, by any function anywhere in
their scope.
Introduction to classes
A class is a way to bind data and its associated functions together.
It allows the data and functions to be hidden.
A class specification has two parts:
1. class declaration
2. class function definitions
The class declaration specifies the type and scope of its members. The class function
definitions describe how the class functions are implemented.
Class declaration
class class_name
{
private:
var-declarations;
func-declarations;
3

public:
var-declarations;
func-declarations;
};
The keyword class specifies, that what follows is an abstract data of type class_name.
The body of a class is enclosed within braces and terminated by a semicolon.
The class body contains the declaration of variables and functions.
These functions and variables are collectively called class members.
The keywords private and public are called visibility labels.
The class members that has been declared private can be accessed only from within the
class.
Public data members can be accessed from outside the class also.
The data hiding using private declaration is a key feature of OOP
The use of keyword private is optional.
The variables declared inside the class are known as data members and the functions are
known as member functions.
Only the member functions have access to the private data members and private member
functions.
Public members can be accessed from outside the class.
Eg:-
class item
{
int number;
float cost;
public:
void getdata( int a, int b);
4

void putdata( void);
};
Creating objects
After declaring a class, we can create variables of that type by using class name.
item x; // memory for x is created
In C++ class variables are known as objects.
We can declare more than one objects in one statement.
item x, y, z;
The necessary memory space is allotted for an object during its declaration.
Class specification provides only a template and does not create any memory space for
the objects.
Objects can also be created when a class is defined .
class item
{



} x, y, z;
Accessing Class Members
The format of calling a member function is
obj_name.func_name(arguments);
Eg:-
x.getdata(100,75.5);
The statement
getdata(100,75.5);
5

is illegal
x.number = 100; // illegal
number is a private data and can be accessed only through member functions.
It cannot be accessed by objects directly.
Defining member functions
Member functions can be defined in two places.
1. outside class definition
2. inside class definition
Outside class definition
Member functions that are declared inside a class have to be defined separately outside
the class.
The general format is
return-type class_name :: func_name( argument declaration)
{
function body
}
The membership label class_name :: tells the compiler that the func_name belongs to the
class_name.
:: scope resolution operator

void item :: getdata(int a, int b)
{
number = a;
cost = b;
}

6

Inside class definition
class item
{
int number;
float cost;
public:
void getdata(int a, int b);
void putdata(void) // inline function
{
cout<<cost<<number;
}
};
When a function is defined inside a class, it is treated as an inline function
Class Implementation
#include<iostream.h>
class item
{
int num;
int cost;
public:
void getdata(int a, int b);
void putdata()
{
cout<< number=<<num;
cout<<cost=<<cost;
7

}};
void item :: getdata(int a, int b)
{
num = a;
cost = b;
}
void main()
{
item x; // create obj x
cout<<object x;
x.getdata(100,200);
x.putdata();
}
Private member functions
A private member function can only be called by another function that is a member of the
class.
An object cannot invoke a private function using dot operator.
class sample
{
int m;
void read(void);
public:
void update( );
void write( );
} s1;
8

s1.read( ); // will not work, objects cannot access private members
However the function read() can be called by the function update().
void sample :: update()
{
read(); // simple call, no objects used
}
MEMORY ALLOCATION OF OBJECTS
Memory space for objects is allotted when they are declared.
But the member functions are created and placed in the memory only once when they are
defined.
No separate space is created for member functions when the objects are created.
Only space for member variables is allocated separately.

Static data members
A static member variable has certain special characteristics.
1. It is initialized to zero when the first object of that class is created.
2. Only one copy of that member is created for the entire class.
3. It is shared by all the objects of that class.
4. It is visible only within the class, but its lifetime is the entire program.
9


#include<iostream.h>
class item
{
static int count;
int number;
public:
void getdata(int a) {
number = a;
count++;
}
void getcount() {
cout<<count<<count;
}
};
int item :: count; //definition of static data member
void main( )
{
item a, b, c;
a.getcount( );
b.getcount( );
c.getcount( );

a.getdata(100);
b.getdata(200);
10

c.getdata(300);
cout<<after reading data;
a.getcount( );
b.getcount( );
c.getcount( );
}

A static data member must be defined outside the class definition.
This is necessary because static data members are stored separately rather than as part of
the object.
Since they are associated with the class itself they are also known as class variables.
Some initial values can also be assigned to static variable
int item :: count = 10;
Static Member Functions
A static member function has the following properties:
1. A static function can have access to only other static members (functions/ variables)
declared within the same class.
2. A static member function can be called using the class name instead of its objects
class_name ::func_name;
#include<iostream.h>
class test
{
11

static int count;
int code;
public:
void setcode() {
code = count++;
}
void showcode () {
cout<<obj no:<<code;
}
static void showcount() {
cout<<count<<count;
}
};
int test :: count;
void main( )
{
test t1, t2;
t1.setcode();
t2.setcode();

test :: showcount();
test t3;
t3.setcode();
test :: showcount();
t1.showcode();
12

t2.showcode();
t3.showcode();
}

Array of objects
We can have array of variables that are of type class.
Such variables are called array of objects.
E.g.:-
class employee
{
char name[20];
float age;
public:
void getdata();
void putdata();
};
employee manager[3]; //array of managers
employee worker[75]; //array of workers
The array manager contains 3 objects:
manager[0], manager[1], manager[2]
The individual elements can be accessed using dot operator.
13

manager[0].putdata();
manager[1].getdata();
The array of objects are stored in the memory in the same way as multidimensional array.
name
manager[0]
age
name
manager[1]
age
name
manager[2]

age

Friend functions
A non-member function can be made friendly to a class so that it can access the private
data of the class.
To make an outside function friendly to a class we have to simply declare the function as
a friend of the class as shown:
14

class ABC
{


public:
friend void xyz(); //declaration
};
The function declaration should be preceded by a keyword friend.
The function is defined elsewhere in the program like a normal C++ function.
The functions that are declared with a keyword friend are known as friend functions.
A function can be declared friend in any number of classes.
characteristics of friend functions
It is not in the scope of the class to which it has been declared as friend.
So it cannot be called using the object of that class.
It can be invoked like a normal function.
It cannot access the class members directly.
It has to use an object name and dot operator for accessing class members.
It can be declared in the public or private part without affecting its meaning.
Usually it has objects as its arguments.
friend function
#include<iostream.h>
class sample
{
int a;
int b;
15

public:
void setvalue()
{
a=25;
b=40;
}
friend float mean(sample s);
};
float mean( sample s)
{
return float(s.a + s.b)/2.0;
}
void main( )
{
sample x;
x.setvalue();
cout<<mean value=;
cout<<mean(x);
}
characteristics of friend functions
Member functions of one class can be friend function of another class.
In such case they are defined using scope resolution operator.
16

class X
{
..

int func1( );
};
class Y
{
..

friend int X :: func1( );

};

Friend class
We can also declare all the member functions of one class as friend functions of another
class.
In such cases, the class is called a friend class.
class Z
{

friend class X; //all member functions of X are
friends to Z
};
Constructors
A constructor is a special member function whose task is to initialize the objects of its
class.
Its name is same as class name.
A constructor is invoked whenever an object of its associated class is created.
It is called constructor because it constructs the value of the data members of the class.
class integer
{
17

int m, n;
public:
integer();
.
.
};
integer :: integer()
{
m=0;
n=0;
}
E.g.:-
integer int1;
not only creates the object int1 but also initializes its data member to zero.
A constructor that accepts no parameter is called the default constructor.
The default constructor for class A is
A::A()
If no such constructor is defined then the compiler supplies a default constructor.
Characteristics of constructors
They should be declared in public section.
They are invoked automatically when the objects are created.
They dont have return types, not even void and they cannot return values.
They cannot be inherited.
They can have default arguments
We cannot refer to their addresses.
18

An object with constructor cannot be used as a member of a union.
They make implicit calls to the operators new and delete when memory allocation is
required.
Parameterized constructors
The constructors that can take arguments are called parameterized constructors.
class integer
{
int m, n;
public:
integer(int x, int y);
.
.
};
When a constructor is parameterized the object declaration
integer int1;
does not work.
We must pass the initial values as arguments to the constructor when the object is declared.
This can be done in 2 ways:
1. by calling constructor explicitly
2. by calling constructor implicitly
integer int1 = integer(0, 100); //explicit call
integer int1(0,100); //implicit call
constructors
#include<iostream.h>
integer ::
integer(int
x, int y)
{
m=x;
n=y;
}
19

class integer
{
int m,n;
public:
integer(int, int) ;
void display()
{
cout<<m=<<m;
cout<<n=<<n;
}
};
integer :: integer(int x, int y)
{
m=x;
n=y;
}
void main( )
{
integer int1(0,100);
integer int2 = integer(25,75);
cout<<OBJ1 ;
int1.display();
cout<<OBJ2;
int2.display();}
20


Constructors with default arguments
It is possible to define constructors with default arguments.
complex(float real, float imag=0);
The default value of the argument imag is 0. the statement
complex(5.0);
assigns the value 5.0 to the real variable and 0.0 to imag (by default).
It is important to distinguish between the default constructor A::A() and the default argument constructor A::A(int x =0)
The default argument constructor can be called with either one argument or no arguments.
When called with no arguments it becomes a default constructor.
When both these forms are used in a class it causes ambiguity for a statement such as
A a;
Constructor overloading
The use of multiple constructors in a same class is called constructor overloading.
21

class integer
{
int m, n;
public:
integer(int x, int y)
{
m=x;
n= y;
}

integer()
{
m=0;
n=0;
}
};
Copy constructor
A copy constructor is used to declare and initialize an object from another object.
integer( integer &i);
The statement
integer i2(i1);
will create an object i2 and at the same time initialize it to the value of i1.
Another form of this statement is
integer i2 = i1;
The process of initializing through a copy constructor is known as copy
initialization.
A copy constructor takes a reference to an object of the same class as itself as an
argument.
Destructor

22

A destructor is used to destroy the objects that have been created by a constructor.
A destructor is a member function whose name is same as class name but is
preceded by a tilde ~.
E.g.:-
~integer() { }
A destructor never takes any argument nor returns any value.
It will be invoked implicitly by the compiler upon exit from a program to clean up
storage that is no longer accessible.
constant member functions
If a member function does not alter any data in the class, then we may declare it as
constant member function.

void mul(int, int) const;
double get_balance() const;
The qualifier const is appended to the function prototypes in both declaration and
definition.
The compiler will generate an error message if such functions try to alter the data
values.
constant objects
We may create and use constant objects using keyword const before object
declaration.
E.g.:-
const matrix X(m,n); //object X is a constant
Any attempt to modify the values of m and n will generate compile time error.
A constant objects can call only constant member functions
Dynamic memory allocation
23

The process of allocating memory during runtime is called dynamic memory
allocation.
During dynamic memory allocation the amount of memory allocated is not known
before use.
The dynamic memory can be allocated from or returned to the system according to
the needs of a running program
The 2 operators new and delete are used for dynamic memory allocation
new operator
The new operator is used to allocate memory dynamically.
A data object created inside a block with new will remain in existence until it is
explicitly destroyed using delete.
The new operator can be used to create objects of any type. The general form is
pointer_variable = new data_type;
The new operator allocates sufficient memory to hold a data object of data_type
and returns the address of the object.
The pointer_variable holds the address of the memory space allocated.
E.g.:-
int *p;
p = new int;
float *f = new float;
Subsequently the statements
*p = 25;
*f = 7.5;
assign 25 to newly allocated int object and 7.5 to the float object.
We can also initialize the memory using new operator
int *p = new int(25);
24

float *f = new float(7.5);
new can also be used to create memory space for user-defined data types such as
arrays, structures and classes.
The general form of a one-dimensional array is
pointer_variable = new data_type[size];
E.g.:-
int *p = new int[10];
creates a memory space for an array of 10 integers. p[0] will refer to the first
element , p[1] to the second element and so on.
When creating multi-dimensional arrays all array sizes must be supplied.
The first dimension may be a variable whose value is supplied at runtime.
All others must be constants
array_ptr = new int[3][5][4]; //legal
array_ptr = new int[m][5][4]; //legal
array_ptr = new int[3][5][ ]; //illegal
array_ptr = new int[ ][5][4]; //illegal
If sufficient memory is not available for allocation then new returns a null pointer.
So it is good to check for the pointer produced by new before using it.


p = new int;
if(!p)
cout<<allocation failed;

25


Advantages of new over malloc()
It automatically computes the size of the data object. We need not use the operator
sizeof.
It automatically returns the correct pointer type, so there is no need of type cast.
It is possible to initialize the object while creating the memory space.
Like any other operator new and delete can be overloaded.
Delete operator
When a data object is no longer needed, it is destroyed to release the memory space
for reuse.
The general form is:
delete pointer_variable;
E.g.:-
delete p;
delete f;
If we want to free the dynamically allocated array the general form is:
delete[size] pointer_variable;
Dynamic constructors
The constructors can also be used to allocate the memory while creating objects.
This will enable the system to allocate the right amount of memory for each object
when the objects are not of the same size, thus resulting in saving of memory.
Allocation of memory to objects at the time of their construction (using new) is
called dynamic construction of objects.
#include<iostream.h>
#include<string.h>
26

class string
{
char *name;
int length;
public:
string()
{
length = 0;
name = new char[length+1];
}

string(char*s)
{
length = strlen(s);
name = new char[length+1];
strcpy(name,s);
}
void display()
{
cout<<name<<\n;
}
void join(string &a, string &b);
};
void string::join(string &a, string &b)
27

{
length = a.length+b.length;
delete name;
name = new char[length+1];
strcpy(name, a.name);
strcpy(name, b.name);
}
void main()
{
char *first = joseph;
string name1(first), name2(louis), s1;
s1.join(name1, name2);
name1.display(); name2.display();
s1.display(); }
Constructors
Constructors are methods which are used to initialize an object at its definition time. We
extend our class Point such that it initializes a point to coordinates (0, 0):
class Point {
int _x, _y;

public:
Point() {
_x = _y = 0;
}

void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Constructors have the same name of the class (thus they are identified to be constructors).
They have no return value. As other methods, they can take arguments. For example, we
28

may want to initialize a point to other coordinates than (0, 0). We therefore define a second
constructor taking two integer arguments within the class:

class Point {
int _x, _y;

public:
Point() {
_x = _y = 0;
}
Point(const int x, const int y) {
_x = x;
_y = y;
}

void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Constructors are implicitly called when we define objects of their classes:
Point apoint; // Point::Point()
Point bpoint(12, 34); // Point::Point(const int, const int)
With constructors we are able to initialize our objects at definition time. We are now able
to define a class List where the constructors take care of correctly initializing its objects.
If we want to create a point from another point, hence, copying the properties of one object
to a newly created one, we sometimes have to take care of the copy process. For example,
consider the class Listwhich allocates dynamically memory for its elements. If we want to
create a second list which is a copy of the first, we must allocate memory and copy the
individual elements. In our class Point we therefore add a third constructor which takes
care of correctly copying values from one object to the newly created one:

class Point {
int _x, _y;

public:
Point() {
_x = _y = 0;
}
Point(const int x, const int y) {
_x = x;
_y = y;
}
Point(const Point &from) {
_x = from._x;
29

_y = from._y;
}

void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
The third constructor takes a constant reference to an object of class Point as an argument
and assigns _x and _y the corresponding values of the provided object.
This type of constructor is so important that it has its own name: copy constructor. It is
highly recommended that you provide for each of your classes such a constructor, even if it
is as simple as in our example. The copy constructor is called in the following cases:
Point apoint; // Point::Point()
Point bpoint(apoint); // Point::Point(const Point &)
Point cpoint = apoint; // Point::Point(const Point &)
With help of constructors we have fulfilled one of our requirements of implementation of
abstract data types: Initialization at definition time. We still need a mechanism which
automatically ``destroys'' an object when it gets invalid (for example, because of leaving its
scope). Therefore, classes can define destructors.

Destructors
Consider a class List. Elements of the list are dynamically appended and removed. The
constructor helps us in creating an initial empty list. However, when we leave the scope of
the definition of a list object, we must ensure that the allocated memory is released. We
therefore define a special method called destructor which is called once for each object at
its destruction time:
void foo() {
List alist; // List::List() initializes to
// empty list.
... // add/remove elements
} // Destructor call!
Destruction of objects take place when the object leaves its scope of definition or is
explicitly destroyed. The latter happens, when we dynamically allocate an object and
release it when it is no longer needed.
Destructors are declared similar to constructors. Thus, they also use the name prefixed by a
30

tilde (~ ) of the defining class:
class Point {
int _x, _y;

public:
Point() {
_x = _y = 0;
}
Point(const int x, const int y) {
_x = xval;
_y = yval;
}
Point(const Point &from) {
_x = from._x;
_y = from._y;
}

~Point() { /* Nothing to do! */ }

void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Destructors take no arguments. It is even invalid to define one, because destructors are
implicitly called at destruction time: You have no chance to specify actual arguments.

Dynamic Memory
Until now, in all our programs, we have only had as much memory available as we
declared for our variables, having the size of all of them to be determined in the source
code, before the execution of the program. But, what if we need a variable amount of
memory that can only be determined during runtime? For example, in the case that we
need some user input to determine the necessary amount of memory space.

The answer is dynamic memory, for which C++ integrates the operators new and delete.


Operators new and new[]
In order to request dynamic memory we use the operator new. new is followed by a data
type specifier and -if a sequence of more than one element is required- the number of these
within brackets []. It returns a pointer to the beginning of the new block of memory
31

allocated. Its form is:

pointer = new type
pointer = new type [number_of_elements]

The first expression is used to allocate memory to contain one single element of type type.
The second one is used to assign a block (an array) of elements of type type,
where number_of_elements is an integer value representing the amount of these. For
example:

1
2
int * bobby;
bobby = new int [5];


In this case, the system dynamically assigns space for five elements of type int and returns
a pointer to the first element of the sequence, which is assigned to bobby. Therefore,
now, bobby points to a valid block of memory with space for five elements of type int.



The first element pointed by bobby can be accessed either with the expression bobby[0] or
the expression *bobby. Both are equivalent as has been explained in the section about
pointers. The second element can be accessed either with bobby[1] or *(bobby+1) and so
on...

You could be wondering the difference between declaring a normal array and assigning
dynamic memory to a pointer, as we have just done. The most important difference is that
the size of an array has to be a constant value, which limits its size to what we decide at the
moment of designing the program, before its execution, whereas the dynamic memory
allocation allows us to assign memory during the execution of the program (runtime) using
any variable or constant value as its size.

The dynamic memory requested by our program is allocated by the system from the
memory heap. However, computer memory is a limited resource, and it can be exhausted.
32

Therefore, it is important to have some mechanism to check if our request to allocate
memory was successful or not.

C++ provides two standard methods to check if the allocation was successful:

One is by handling exceptions. Using this method an exception of type bad_alloc is thrown
when the allocation fails. Exceptions are a powerful C++ feature explained later in these
tutorials. But for now you should know that if this exception is thrown and it is not handled
by a specific handler, the program execution is terminated.

This exception method is the default method used by new, and is the one used in a
declaration like:

bobby = new int [5]; // if it fails an exception is thrown


The other method is known as nothrow, and what happens when it is used is that when a
memory allocation fails, instead of throwing a bad_alloc exception or terminating the
program, the pointer returned by new is a null pointer, and the program continues its
execution.

This method can be specified by using a special object called nothrow, declared in
header <new>, as argument for new:

bobby = new (nothrow) int [5];


In this case, if the allocation of this block of memory failed, the failure could be detected
by checking if bobby took a null pointer value:

1
2
3
4
int * bobby;
bobby = new (nothrow) int [5];
if (bobby == 0) {
// error assigning memory. Take measures.
};
33

5


This nothrow method requires more work than the exception method, since the value
returned has to be checked after each and every memory allocation, but I will use it in our
examples due to its simplicity. Anyway this method can become tedious for larger projects,
where the exception method is generally preferred. The exception method will be
explained in detail later in this tutorial.


Operators delete and delete[]
Since the necessity of dynamic memory is usually limited to specific moments within a
program, once it is no longer needed it should be freed so that the memory becomes
available again for other requests of dynamic memory. This is the purpose of the
operator delete, whose format is:

1
2
delete pointer;
delete [] pointer;


The first expression should be used to delete memory allocated for a single element, and
the second one for memory allocated for arrays of elements.

The value passed as argument to delete must be either a pointer to a memory block
previously allocated with new, or a null pointer (in the case of a null
pointer, deleteproduces no effect).

1
2
3
4
5
6
7
// rememb-o-matic
#include <iostream>
#include <new>
using namespace std;

int main ()
{
int i,n;
int * p;
34

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cout << "How many numbers would you like to type? ";
cin >> i;
p= new (nothrow) int[i];
if (p == 0)
cout << "Error: memory could not be allocated";
else
{
for (n=0; n<i; n++)
{
cout << "Enter number: ";
cin >> p[n];
}
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << p[n] << ", ";
delete[] p;
}
return 0;
}


Notice how the value within brackets in the new statement is a variable value entered by
the user (i), not a constant value:

p= new (nothrow) int[i];


But the user could have entered a value for i so big that our system could not handle it. For
example, when I tried to give a value of 1 billion to the "How many numbers" question, my
system could not allocate that much memory for the program and I got the text message we
prepared for this case (Error: memory could not be allocated). Remember that in the case
that we tried to allocate the memory without specifying the nothrow parameter in the new
expression, an exception would be thrown, which if it's not handled terminates the
program.
35


It is a good practice to always check if a dynamic memory block was successfully
allocated. Therefore, if you use the nothrow method, you should always check the value of
the pointer returned. Otherwise, use the exception method, even if you do not handle the
exception. This way, the program will terminate at that point without causing the
unexpected results of continuing executing a code that assumes a block of memory to have
been allocated when in fact it has not.


Dynamic memory in ANSI-C

Operators new and delete are exclusive of C++. They are not available in the C language.
But using pure C language and its library, dynamic memory can also be used through the
functions malloc, calloc, realloc and free, which are also available in C++ including
the <cstdlib> header file (see cstdlib for more info).

The memory blocks allocated by these functions are not necessarily compatible with those
returned by new, so each one should be manipulated with its own set of functions or
operators.
Friend functions
In principle, private and protected members of a class cannot be accessed from outside the
same class in which they are declared. However, this rule does not affectfriends.

Friends are functions or classes declared with the friend keyword.

If we want to declare an external function as friend of a class, thus allowing this function to
have access to the private and protected members of this class, we do it by declaring a
prototype of this external function within the class, and preceding it with the
keyword friend:

1
2
3
4
5
6
// friend functions
#include <iostream>
using namespace std;

class CRectangle {
int width, height;
public:
36

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

void set_values (int, int);
int area () {return (width * height);}
friend CRectangle duplicate (CRectangle);
};

void CRectangle::set_values (int a, int b) {
width = a;
height = b;
}

CRectangle duplicate (CRectangle rectparam)
{
CRectangle rectres;
rectres.width = rectparam.width*2;
rectres.height = rectparam.height*2;
return (rectres);
}

int main () {
CRectangle rect, rectb;
rect.set_values (2,3);
rectb = duplicate (rect);
cout << rectb.area();
return 0;
}


The duplicate function is a friend of CRectangle. From within that function we have been
able to access the members width and height of different objects of typeCRectangle, which
are private members. Notice that neither in the declaration of duplicate() nor in its later use
in main() have we considered duplicate a member of classCRectangle. It isn't! It simply
has access to its private and protected members without being a member.

The friend functions can serve, for example, to conduct operations between two different
classes. Generally, the use of friend functions is out of an object-oriented programming
methodology, so whenever possible it is better to use members of the same class to
perform operations with them. Such as in the previous example, it would have been shorter
to integrate duplicate() within the class CRectangle.

37

Advantages & Disadvantages of friend function

Using friend functions provide a degree of freedom in the interface design options.

Member functions and friend functions are equally privileged (100% vested). The major
difference is that a friendfunction is called like f(x), while a member function is called
like x.f(). Thus the ability to choose between member functions (x.f()) and friend functions
(f(x)) allows a designer to select the syntax that is deemed most readable, which lowers
maintenance costs. The major disadvantage of friend functions is that they require an extra
line of code when you want dynamic binding. To get the effect of a virtual friend,
the friend function should call a hidden (usually protected) virtual member function. This
is called the Virtual Friend Function Idiom. For example:


class Base {
public:
friend void f(Base& b);
...
protected:
virtual void do_f();
...
};

inline void f(Base& b)
{
b.do_f();
}

class Derived : public Base {
public:
...
protected:
virtual void do_f(); // "Override" the behavior of f(Base& b)
...
};

void userCode(Base& b)
{
f(b);
}
The statement f(b) in userCode(Base&) will invoke b.do_f(), which is virtual. This means
thatDerived::do_f() will get control if b is actually a object of class Derived. Note
that Derived overrides the behavior of the protected virtual member function do_f(); it
38

does not have its own variation of the friend function,f(Base&).
Friend classes
Just as we have the possibility to define a friend function, we can also define a class as
friend of another one, granting that first class access to the protected and private members
of the second one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// friend class
#include <iostream>
using namespace std;

class CSquare;

class CRectangle {
int width, height;
public:
int area ()
{return (width * height);}
void convert (CSquare a);
};

class CSquare {
private:
int side;
public:
void set_side (int a)
{side=a;}
friend class CRectangle;
};

void CRectangle::convert (CSquare a) {
width = a.side;
height = a.side;
}

int main () {
CSquare sqr;
CRectangle rect;
sqr.set_side(4);
rect.convert(sqr);
cout << rect.area();
return 0;
}
39

31
In this example, we have declared CRectangle as a friend of CSquare so
that CRectangle member functions could have access to the protected and private members
ofCSquare, more concretely to CSquare::side, which describes the side width of the square.

You may also see something new at the beginning of the program: an empty declaration of
class CSquare. This is necessary because within the declaration of CRectanglewe refer to
CSquare (as a parameter in convert()). The definition of CSquare is included later, so if we
did not include a previous empty declaration for CSquare this class would not be visible
from within the definition of CRectangle.

Consider that friendships are not corresponded if we do not explicitly specify so. In our
example, CRectangle is considered as a friend class by CSquare, but CRectangle does not
consider CSquare to be a friend, so CRectangle can access the protected and private
members of CSquare but not the reverse way. Of course, we could have declared
also CSquare as friend of CRectangle if we wanted to.

Another property of friendships is that they are not transitive: The friend of a friend is not
considered to be a friend unless explicitly specified.
Abstract classes
An abstract class is a class that is designed to be specifically used as a base class. An
abstract class contains at least one pure virtual function. You declare a pure virtual
function by using a pure specifier (= 0) in the declaration of a virtual member function in
the class declaration.
The following is an example of an abstract class:
class AB {
public:
virtual void f() = 0;
};
Function AB::f is a pure virtual function. A function declaration cannot have both a pure
specifier and a definition. For example, the compiler will not allow the following:
struct A {
virtual void g() { } = 0;
};
40

You cannot use an abstract class as a parameter type, a function return type, or the type of
an explicit conversion, nor can you declare an object of an abstract class. You can,
however, declare pointers and references to an abstract class. The following example
demonstrates this:
struct A {
virtual void f() = 0;
};

struct B : A {
virtual void f() { }
};

// Error:
// Class A is an abstract class
// A g();

// Error:
// Class A is an abstract class
// void h(A);
A& i(A&);

int main() {

// Error:
// Class A is an abstract class
// A a;

A* pa;
B b;

// Error:
// Class A is an abstract class
// static_cast<A>(b);
}
Class A is an abstract class. The compiler would not allow the function declarations A
g() or void h(A), declaration of object a, nor the static cast of b to type A.
Virtual member functions are inherited. A class derived from an abstract base class will
also be abstract unless you override each pure virtual function in the derived class.
For example:
41

class AB {
public:
virtual void f() = 0;
};

class D2 : public AB {
void g();
};

int main() {
D2 d;
}
The compiler will not allow the declaration of object d because D2 is an abstract class; it
inherited the pure virtual function f()from AB. The compiler will allow the declaration of
object d if you define function D2::g().
Note that you can derive an abstract class from a nonabstract class, and you can override a
non-pure virtual function with a pure virtual function.
You can call member functions from a constructor or destructor of an abstract class.
However, the results of calling (directly or indirectly) a pure virtual function from its
constructor are undefined. The following example demonstrates this:
struct A {
A() {
direct();
indirect();
}
virtual void direct() = 0;
virtual void indirect() { direct(); }
};
The default constructor of A calls the pure virtual function direct() both directly and
indirectly (through indirect()).
The compiler issues a warning for the direct call to the pure virtual function, but not for the
indirect call.
Static data members
The declaration of a static data member in the member list of a class is not a definition.
You must define the static member outside of the class declaration, in namespace scope.
For example:
42

class X
{
public:
static int i;
};
int X::i = 0; // definition outside class declaration
Once you define a static data member, it exists even though no objects of the static data
member's class exist. In the above example, no objects of class Xexist even though the
static data member X::i has been defined.
Static data members of a class in namespace scope have external linkage. The initializer for
a static data member is in the scope of the class declaring the member.
A static data member can be of any type except for void or void qualified
with const or volatile. You cannot declare a static data member as mutable.
You can only have one definition of a static member in a program. Unnamed classes,
classes contained within unnamed classes, and local classes cannot have static data
members.
Static data members and their initializers can access other static private and protected
members of their class. The following example shows how you can initialize static
members using other static members, even though these members are private:
class C {
static int i;
static int j;
static int k;
static int l;
static int m;
static int n;
static int p;
static int q;
static int r;
static int s;
static int f() { return 0; }
int a;
public:
C() { a = 0; }
};
C c;
int C::i = C::f(); // initialize with static member function
int C::j = C::i; // initialize with another static data member
int C::k = c.f(); // initialize with member function from an object
43

int C::l = c.j; // initialize with data member from an object
int C::s = c.a; // initialize with nonstatic data member
int C::r = 1; // initialize with a constant value
class Y : private C {} y;

int C::m = Y::f();
int C::n = Y::r;
int C::p = y.r; // error
int C::q = y.f(); // error
The initializations of C::p and C::q cause errors because y is an object of a class that is
derived privately from C, and its members are not accessible to members of C.
If a static data member is of const integral or const enumeration type, you may specify
a constant initializer in the static data member's declaration. This constant initializer must
be an integral constant expression. Note that the constant initializer is not a definition. You
still need to define the static member in an enclosing namespace. The following example
demonstrates this:
#include <iostream>
using namespace std;

struct X {
static const int a = 76;
};

const int X::a;

int main() {
cout << X::a << endl;
}
The tokens = 76 at the end of the declaration of static data member a is a constant
initializer.

Add and multiply two fractional numbers

import fractions

class FractionClass:
# Initialize starting numerator and denominator.
n = 0
44

d = 0
def __init__(self, numerator, denominator):
self.n = numerator
self.d = denominator

# Function that adds fractions, whichs throws NameError if gcd() doesn't work!
def add_fractions(self, other):
try:
sum_numerator = self.n + other.n
sum_denominator = fractions.gcd(self.d, other.d)
return(sum_numerator, sum_denominator)
except NameError:
print("Methods aren't defined.")

# Function that multiplies fractions, whichs throws NameError if gcd() doesn't work!
def multiply_fractions(self, other):
try:
product_numerator = self.n * other.n
product_denominator = self.d * other.d
return(product_numerator, product_denominator)
except NameError:
print("Methods aren't defined.")

# Enter input function.
def user_input():
try:
print("Enter a numerator and denominator:")
n, d = [int(x) for x in input().split()]
print("Enter a numerator and denominator:")
n2, d2 = [int(x) for x in input().split()]
# Check used to debug for denominators that aren't the minimum of 1 (0 can't be
divided!)
check = 1 / d
check = 1 / d2
# print(check)

# Exception for d = 0.
except ZeroDivisionError:
print("\n You didn't enter the minimum denominator.")
print("Set denominator to minimum default.")
d = 1
45

# Exception for not entering a space in between numbers.
except UnboundLocalError:
print("You didn't enter your numbers in properly! Try again.")
# Exception for not entering all required.
except NameError:
print("\n You didn't enter two numbers.")
# Exception for user input both or one of them, not being integers.
except TypeError:
print("\n You didn't enter all valid numbers.")
# General exception case.
except:
print("Something went wrong!")

fract = FractionClass(n,d)
another_fraction = FractionClass(n2, d2)
total_sum = fract.add_fractions(another_fraction)
# Unpacks total sum tuple.
# Puts in fraction format.
sum_numerator, sum_denominator = total_sum
add_output = fractions.Fraction(sum_numerator, sum_denominator)
total_product = fract.multiply_fractions(another_fraction)
# Unpacks product sum tuple.
# Puts in fraction format.
product_numerator, product_denominator = total_product
multiply_output = fractions.Fraction(product_numerator, product_denominator)
print(add_output, multiply_output)

Define a class to represent a bank account. Include the following
members:

Data Members
1 Name of the depositor
2 Account Number
3 Type of account
4 Balance amount in the account

Member function
1 To assign initial values
2 To deposit an amount
46

3 To withdraw an amount after checking the balance
4 To display name and Balance

write a main program to test the program. */

#include
#include
#include

class bank
{
char name[20];
int acno;
char actype[20];
int bal;
public :
void opbal(void);
void deposit(void);
void withdraw(void);
void display(void);
};

void bank :: opbal(void)
{
cout<<<<"Enter Name :-"; cin>>name;
cout<<"Enter A/c no. :-"; cin>>acno;
cout<<"Enter A/c Type :-"; cin>>actype;
cout<<"Enter Opening Balance:-"; cin>>bal;
}

void bank :: deposit(void)
{
cout<<"Enter Deposit amount :-"; int deposit=0; cin>>deposit;
deposit=deposit+bal;
cout<<"\nDeposit Balance = "<<<"\nBalance Amount = "<<<"\nEnter Withdraw Amount
:-"; cin>>withdraw;
bal=bal-withdraw;
cout<<"After Withdraw Balance is "<<<<<<<"DETAILS"<<<<"name "<<<<<"A/c. No.
"<<<<<"A/c Type "<<<<<"Balance "<<<<"\n\nChoice List\n\n"; cout<<"1) To assign
47

Initial Value\n"; cout<<"2) To Deposit\n"; cout<<"3) To Withdraw\n"; cout<<"4) To
Display All Details\n"; cout<<"5) EXIT\n"; cout<<"Enter your choice :-"; cin>>choice;
switch(choice)
{
case 1: o1.opbal();
break;
case 2: o1.deposit();
break;
case 3: o1.withdraw();
break;
case 4: o1.display();
break;
case 5: goto end;
}
}while(1);
end:
}

this pointer
C++ uses a unique keyword called this to represent an object that invokes a member
function. this is a pointer that points to the object for which this function was called. For
example, the function call A.max() will set the pointer this to the address of the object A.
The starting address is the same as the address of the first variable in the class structure.
This unique pointer is automatically passed to a member function when it is called. The
pointer this acts as an implicit argument to all the member functions. Consider the
following simple example:
Class ABC
{
int a;

.
};
48

The private variable a can be used directly inside a member function, like
a=123;
We can also use the following statement to do the same job:
this-> = 123;
Since C++ permits the use of shorthand from a=123, we have not been using the pointer
this explicitly so far. However, we have been implicitly using the pointer this when
overloading the operators using member function.
When a binary operator is overloaded using a member function, we pass only one
argument to the function. The other argument is implicitly passed using the pointer this.
One important application of the pointer this is to return the object it points to. For
example, the statement
return *this;
inside a member function will return the object that invoked the function. This statement
assumes importance when we want to compare two or more objects inside a member
function and return the invoking object as a result. Example:
person & person :: greater(person &x)
{
if x.age >age
return x; //argument object
else
return *this; //invoking object
}
Suppose we invoke this function by call
max = A.greater(B);
The function will return the object B (argument object) if the age of the person B is greater
than that of A, otherwise, it will return the object A (invoking object) using the pointer this.
#include<iostream.h>
49

#include<string.h>
class per
{
char name[20];
float saralry;
public :
per (char *s,float a)
{strcpy(name,s); salary =a}
per GR(per & x)
{ if (x.salary> =salary)
return &x;
else
return this;
}
void display()
{
cout<<name : <<name<<\n;
cout<<salar :<<salary<<\n;
}
};
Void main ()
{
Per p1(REEMA:, 10000), p2(KRISHANAN,20000), p3
(GEORGE, 50000);
The output of the Program would be :
Name : REEMA
Salary : 10000
Name : KRISHANAN
50

Salary : 20000
Here, the first call to the function GR returns reference to the object P1 and the second
call returns reference to the object P2.
Using new, show how to allocate a double and give it an initial
value of 123.9784.
#include <iostream>
using namespace std;

int main ()
{
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable

*pvalue = 123.9784; // Store value at allocated address
cout << "Value of pvalue : " << *pvalue << endl;

}




The advantages of using new and delete over their older relatives
malloc/free are the following:

new and delete point to the correct memory type (they are type safe), so casts are not
necessary.
new invokes the constructor, allowing the initialization of objects, and delete invokes the
destructor.
new figures out the size it needs to allocate, so the programmer doesn't have to specify it.
new throws an exception of type std::bad_alloc when it fails, instead of only returning a
NULL pointer like malloc, so the programmer doesn't have to write the deprecated check
if(NULL==data) error();
You can specialize the behavior of new by overloading it for your specific class (this will be
discussed further in a following article) or even replacing the global one (this can generate
problems, however, because someone might rely on the default behavior).
overloaded constructors
51

Besides performing the role of member data initialization, constructors are no different from
other functions. This included overloading also. In fact, it is very common to find overloaded
constructors. For example, consider the following program with overloaded

constructors for the figure class :
//Illustration of overloaded constructors
//construct a class for storage of dimensions of circles.
//triangle and rectangle and calculate their area
#include<iostream.h>
#include<conio.h>
#include<math.h>
#include<string.h> //for strcpy()

52

Class figure
{
Private:
Float radius, side1,side2,side3; //data members
Char shape[10];
Public:
figure(float r) //constructor for circle
{
radius=r;
strcpy (shape, circle);
}
figure (float s1,float s2) //constructor for rectangle
strcpy
{
Side1=s1;
Side2=s2;
Side3=radius=0.0; //has no significance in rectangle
strcpy(shape,rectangle);
}
Figure (float s1, floats2, float s3) //constructor for triangle
{
side1=s1;
side2=s2;
side3=s3;
radius=0.0;
strcpy(shape,triangle);
}
void area() //calculate area
{
53

float ar,s;
if(radius==0.0)
{
if (side3==0.0)
ar=side1*side2;
else
ar=3.14*radius*radius;
cout<<\n\nArea of the <<shape<<is :<<ar<<sq.units\n;
}
};
Void main()
{
Clrscr();
Figure circle(10.0); //objrct initialized using constructor
Figure rectangle(15.0,20.6);//objrct initialized using onstructor
Figure Triangle(3.0, 4.0, 5.0); //objrct initialized using
constructor
Rectangle.area();
Triangle.area();
Getch();//freeze the monitror
}

Anda mungkin juga menyukai