Você está na página 1de 21

Object Oriented Programming PART 1

Tutorial in C++
-Sanchit Karve
born2c0de@hotmail.com

.INDEX.
INTRODUCTION TO OOP:
I. Introduction
II. Constructors
II.A |--->Constructors with Arguments
II.B |--->Default Argument Constructors
III. Destructors
IV. The Complex Class
V. Overloading Operators.I
VI. The Copy Constructor
VII. Overloading Operators.II
VIII. The this Pointer
IX. Declaring Data Members Outside the Class
X. The sizeof() Operator
XI. Structures v/s Classes
CLASSES IN DETAIL:
I. Static Data Members INTRODUCTION TO OOP
I. INTRODUCTION
Before I begin, make sure that you at least have a working knowledge of
non-object oriented C++ Programming. If you know object oriented programming in
another language it will be an advantage. Object Oriented Programming is a
rather difficult subject and it requires a new way of thinking to write
programs. So if you don't understand some parts as you read don't worry.....read
it once again till you understand a bit more and then continue reading. As you
keep going ahead concepts will automatically be clear to you.
In Non Object oriented programming problems are based on the algorithimic
point of view. In object oriented programming we have to think from a design
perspective. Non OOP focuses on the algorithm while OOP focuses on data. This
maybe slightly hard to understand as of now but as we continue you will have a
better understanding of object oriented programming.
Let us by this example see how classes function:

#include <iostream.h>
class sample
{
private:
int data;
public:
void fun1()
{
data=15;
}
void fun2()
{
cout<<data;
}
};
void main()
{
sample a;
sample b;
a.fun1();
a.fun2();
b.fun2();
}

Here, a class named sample is provided. A class is a combination of data types


and functions grouped together. Another structure known as an object uses all
the properties and data members that a class contains. So here the class in the
program is 'sample' and it's two objects are 'a' and 'b'. An object is an
instance of a class. For example we can consider a class as a class 'Car'. We
can have many objects such as 'Ferrari' or 'Chevy' or 'Mercedes' since they all
contain all properties of a car.
Let us now look at the declaration of a class. It's syntax is as follows:
class <class_name>
{
<public:> OR <private:> OR <protected:>
// Data Members and Functions
};
The end of a class is always terminated by a ';'.
Data Members and functions are written within the class the same way as they are
written otherwise.
What we must remember is that a class cannot function by itself. Objects must be
created from the classes for the classes to be functional. The process of
creating objects from classes is known as instantiation. It's syntax for
creating an object is:
<class_name> <object_name>;
Consider it similar to creating variables from data types. Infact let us look at
this example. The Declaration 'int a;' (in a usual C++ program) can be seen as
an object 'a' created from the class 'int'.
Whenever a function of a class is to be executed, it's syntax is:
<object_name>.<function(argument1,argument2.....)>;
Similarly Data types can be accessed or changed in the same way by using the '.'
separator after the object. So if a data type 'd' of 'int' is present in a given
class 'sample' then we can modify it as follows:
sample obj1; // Creating the Object
obj1.d=15; // Modifying the value
This can be done only in certain conditions as we shall see later in this
section itself.
Every object is an independant instance of the class and two objects of the same
class need not be similar. Though they are created from a class, the values of
their data members differ. So again we can think of the 'int' data type as a
class 'int' and the variable name as the object. Every variable of the int type
is similar in properties and characteristics but it's value differs from
variable to variable or object to object.
We can even consider the car example. Though objects 'Ferrari' and 'Mercedes'
are created from class 'car' they are similar because both objects have what a
car has ie. a steering wheel , 4 wheels , Brakes etc. but they are different
since the engine power varies from object to object. Ferrari may have 'x' BHP
while the Mercedes may have 'y' BHP. So objects need not be identical due to
their difference in values of it's data members.
Now, let us get back to the program we first wrote. First the program creates
two objects 'a' and 'b' of the sample class. Then it calls the fun1() function
from the object 'a' and fun2() from the same object. The fun1() function sets
the data member 'a.data' to 15 and fun2() outputs 'a.data' ie. 15. However for
the next object 'b' the fun2() function is directly called without initialising
the data member. Hence it's output will be a wierd number since it will try to
output the contents of a variable which is not initialised.
Looking at the program you may wonder what is private/public/protected. These
are keywords in C++ which are also known as access modifiers. They determine
whether a function or a data member can be accessed from inside or outside he
class.
Any function or data member that comes under the 'private' keyword can be
accessed only from inside the class and no where else. Functions and data
members coming under the public keyword can be accessed from inside as well as
outside the class. All this will make sense after you have a look at this
program:
#include <iostream.h>
class sample
{
private:
int d1;
void fun1()
{
cout<<"In fun1()";
}
public:
int d2;
void fun2()
{
cout<<"In fun2()";
d1=10; // works
d2=20; // works
fun1(); // works
}
};
void main()
{
sample a;
a.d1=100; // doesn't work
a.d2=3; // works
a.fun1(); // doesn't work
a.fun2(); // works
}
As you see 'd1' and 'fun1()' cannot be accessed from the main function while 'd2
'
and 'fun2' can be accessed from inside as well as outside the class. This means
that to access or modify a function or data member that is private we must call
another function which modifies the data members which is also present in the
same class but is declared public. Here we cannot call fun1() function directly
from main() as it is a private function. Hence we have called fun2() from main()
to call fun1() since fun2() is a public function. Once control is passed to the
function f2() it can access any data type or function within the class.
The protected keyword has something to do with another advanced object oriented
topic known as inheritance which is also explained in PART 2. Hence I
will describe the 'protected' keyword later.
As you saw the 'private' and 'public' keywords enable a feature known as data
hiding. Do not confuse data hiding with security techniques. Security Techniques
prevent illegal access to be accessed by unauthorised people while data hiding
prevents well intentioned users from accessing 'private' data. Programmers if
desiring to access private data members from outside the class can do so using
a special technique and this can be done intentionally only. Hence C++ ensures
that programmers cannot access private data members outside the class
unintentionally. I am including a program that access private data members
outside the class just for the sake of some people's curiosity but I will not
explain it as it's explanation is faintly or not related to object oriented
programming at all. Here is the program:

#include <iostream.h>
#include <string.h>
class student
{
private:
char name[20];
char grade;
public:
void setname()
{
strcpy(name,"Sanchit");
grade='F';
}
void display()
{
cout<<"Name = "<<name<<endl
<<"Grade = "<<grade<<endl;
}
};
struct access
{
char name[20];
char grade;
};

void main()
{
student s;
s.setname();
s.display();
struct access *a=(access*)&s;
strcpy(a->name,"Sanchit Karve");
a->grade='A';
s.display();
}

II. CONSTRUCTORS
Usually we need to set default values to variables in our classes. For example
if a class uses integers it would be prudent to set the default value of the
integer to 0 before any operation is done on them. The Same way we used the
setname() function to set the values for the data members. Hence the setname()
function has to be called immediately after the object has been created. In Huge
programs where many objects are created we would have to call the initialising
function everytime for each object to set it's default values. This can get a
bit frustrating.
So the Developers of C++ included a feature in object oriented programming known
as constructors. These functions contain all the initialisation code and is
automatically called everytime an object is created. A constructor has the same
name as the class so that it can be easily distinguished from other functions.
Consider this program which implements contructors:
#include <iostream.h>
#include <string.h>
class student
{
private:
char name[20];
char grade;
public:
student()
{
strcpy(name,"Sanchit Karve");
grade='A';
}
void display()
{
cout<<"Name = "<<name<<endl
<<"Grade = "<<grade<<endl;
}
};
void main()
{
student a;
a.display();
}
Here as you saw we created an object as well as initialised it's variables in
just one statement. Here the constructor is immediately called when a student
object 'a' is created and it sets 'name[]' and 'grade' to "Sanchit Karve" and 'A
'
respectively. You will agree that using constructors is a lot more better way
than using another function call to initialise data members.

II.A CONSTRUCTORS WITH ARGUMENTS


Let us consider the previous program. Every time a student object is created the
name data member is set to "Sanchit Karve" everytime. What if you wanted the
name data member to contain any specified value during instantiation? This is
when constructors with arguments come into the picture. Look at the following
program:
#include <iostream.h>
#include <string.h>
class student
{
private:
char name[20];
char grade;
public:
student()
{
strcpy(name,"");
grade='-';
}
student(char *nm,char grd)
{
strcpy(name,nm);
grade=grd;
}
void display()
{
cout<<"Name = "<<name<<endl
<<"Grade = "<<grade<<endl;
}
};
void main()
{
student a("Sanchit Karve",'A');
student b("John Doe",'B');
student c;
a.display();
b.display();
c.display();
}
The Output of the Program is:
Name = Sanchit Karve
Grade = A
Name = John Doe
Grade = B
Name =
Grade = -
The Statement ' student a("Sanchit Karve",'A'); ' calls the two argument
constructor. This constructor works like any other function receiving arguments.
You must be now wondering why I included two constructors in the code. The
reason is simple. Suppose we declare an object without any arguments. Then the
zero argument constructor is called instead of the 2-argument constructor since
the object was instantiated with no arguments.
What happens if we dont include a constructor at all? It doesn't matter since
the compiler automatically inserts an implicit empty constructor while compiling
the code.
II.B DEFAULT ARGUMENT CONSTRUCTORS
In our previous program we wrote two constructors. One would be called if an
object was created with two arguments and the other would be called if an object
was created without specifying any arguments. Wouldn't it be better if we
included all the initialisation code in just one constructor only instead of
coding it in two constructors? We can and have a look how this can be done.
#include <iostream.h>
#include <string.h>
class student
{
private:
char name[20];
char grade;
public:
student(char *nm="",char grd='-')
{
strcpy(name,nm);
grade=grd;
}
void display()
{
cout<<"Name = "<<name<<endl
<<"Grade = "<<grade<<endl;
}
};
void main()
{
student a("Sanchit",'A');
student b("John");
student c;
a.display();
b.display();
c.display();
}
Output:
Name = Sanchit
Grade = A
Name = John
Grade = -
Name =
Grade = -
Look at the default argument constructor.It's declaration states that if an
argument is not provided the default value ie. the value after the '=' sign will
be used. So in 'student b("John");' the first argument is "John" while the
effective second argument will be '-' since it is not given during instantiation
.
In the third statement only the default values are used since no arguments are
specified. With Default Constructors however we cannot skip the first argument.
So saying 'student a(,'D');' will not work.
III. DESTRUCTORS
Just like how a constructor is called everytime an object is created, another
function called the destructor is called everytime when an object loses scope or
gets deallocated.It has the same name as the class but is prefixed by a tilda '~
'
Look at this example.It is very simple to understand.
#include <iostream.h>
class sample
{
public:
sample()
{
cout<<"In Constructor\n";
}
void fun()
{
cout<<"In fun()\n";
}
~sample()
{
cout<<"In Destructor\n";
}
};
void main()
{
sample a;
a.fun();
}
Here the Destructor is called after the object loses scope at the end of main().
The Destructor can also be called by using the delete operator for deallocating
the object.We shall learn about the delete operator later.
IV. THE COMPLEX CLASS
We know know enough to write a simple class that implements complex numbers.
Once you understand the program you will appreciate all the later enhancements
we will make to the following program.Here is the code:
#include <iostream.h>
class complex
{
private:
float real;
float imag;
public:
complex(float r=0.0f,float i=0.0f)
{
real = r;
imag = i;
}
void add(complex a,complex b)
{
real = a.real + b.real;
imag = a.imag + b.imag;
}
void subtract(complex a,complex b)
{
real = a.real - b.real;
imag = a.imag - b.imag;
}
void multiply(complex a,complex b)
{
real = ( a.real * b.real ) - ( a.imag * b.imag );
imag = ( a.real * b.imag ) - ( a.imag * b.real);
}
void divide(complex a,complex b)
{
float div = ( b.real * b.real ) + ( b.imag * b.imag );
real = ( ( a.real * b.real ) + ( a.imag * b.imag ) ) / div;
imag = ( ( a.imag * b.real ) - ( a.real * b.imag ) ) / div;
}
void setdata(float f,float i)
{
real = f;
imag = i;
}
void display()
{
cout<<"Real = "<<real<<endl<<"Imaginary = "<<imag<<endl;
}
};
void main()
{
complex a(1.2,3.6),b;
b.setdata(2.2,3.3);
complex c;
c.add(a,b);
complex d,e;
e.subtract(a,b); // -> 1
d.multiply(c,e); // -> 2
a.display();
b.display();
c.display();
d.display();
e.display();
}
Output:
Real = 1.2
Imaginary = 3.6
Real = 2.2
Imaginary = 3.3
Real = 3.4
Imaginary = 6.9
Real = -5.47
Imaginary = 7.92
Real = -1
Imaginary = 0.3
Here we have performed some basic calculations. First is adding two complex
numbers ie. c.add(a,b) and another calculation which has been done using two
statements ie. the statements which are commented 1 and 2. Mathematically the
operation would be d = c * ( a - b )
Wouldn't it be better if we could write the mathematical operations in a simple
mathematical format instead of using add and subtract functions? You would
certainly agree that the statement " a = b + c " is a lot easier to read and
understand compared to it's equivalent a.add(b,c). Let us see how we can do this
in the coming section.
V. OVERLOADING OPERATORS.I
Let's just start by improving the complex class program as it will make more
sense to explain overloading functions when you have gone through the program.
#include <iostream.h>
class complex
{
private:
float real;
float imag;
public:
complex(float r=0.0f,float i=0.0f)
{
real = r;
imag = i;
}
void setdata(float f,float i)
{
real = f;
imag = i;
}
void display()
{
cout<<"Real = "<<real<<endl<<"Imaginary = "<<imag<<endl;
}
void operator =(complex c)
{
real = c.real;
imag = c.imag;
}
complex operator +(complex c)
{
complex t;
t.real = real + c.real;
t.imag = imag + c.imag;
return t;
}
complex operator -(complex c)
{
complex t;
t.real = real - c.real;
t.imag = imag - c.imag;
return t;
}
complex operator *(complex c)
{
complex tmp;
tmp.real = ( real * c.real ) - ( imag * c.imag );
tmp.imag = ( real * c.imag ) - ( imag * c.real );
return tmp;
}
complex operator /(complex c)
{
float div = ( c.real * c.real ) + ( c.imag * c.imag );
complex tmp;
tmp.real = ( ( real * c.real ) + ( imag * c.imag ) ) / div;
tmp.imag = ( ( imag * c.real ) - ( real * c.imag ) ) / div;
return tmp;
}
};
void main()
{
complex a(1.2,3.6),b;
b.setdata(2.2,3.3);
complex c;
c = a + b;
complex d,e;
e = a - b; // -> 1
d = c * e; // -> 2
a.display();
b.display();
c.display();
d.display();
e.display();
}
The Output is the same as the previous one ie.
Real = 1.2
Imaginary = 3.6
Real = 2.2
Imaginary = 3.3
Real = 3.4
Imaginary = 6.9
Real = -5.47
Imaginary = 7.92
Real = -1
Imaginary = 0.3
As you see the results are the same while the code gets more readable. When we
add two complex numbers like in the case of c = a + b; first the + overloaded
operator function is called and then the overloaded = operator function is
called. In short the compiler interprets c = a + b; like this:
c.operator =(a.operator +(b));
Now you can understand why only 1 argument is declared in the overloaded
functions. It is because the function is called from one object itself while the
other object is taken as the parameter.
Quite a few operators can be overloaded and some of them are as follows:
!,=,+,-,*,/,->,==,>,<,>=,<=,!= etc.
We must note that the '.' operator cannot be overloaded.
The main() code still looks slightly sluggish. Instead of using a.display() etc.
for displaying object values don't you think it would be a lot better if we use
cout<<a. Yes, << and >> can also be overloaded so that cout and and cin can be
used on objects.Overloading the << and >> operators is a lot different than
overloading the other operators because we need a thorough understanding of how
cout and cin work. It's not that hard actually and you will be able to see that
in this example code. Here just add the following function to the previous
complex class and change all the display() to cout. You will see that the code
is easily readable and the results remain the same.Here is the program:
#include <iostream.h>
class complex
{
private:
float real;
float imag;
public:
complex(float r=0.0f,float i=0.0f)
{
real=r;
imag=i;
}
// Other Functions
friend ostream& operator <<(ostream& s,complex c);
};
ostream& operator <<(ostream &s,complex c)
{
s<<"Real = "<<c.real<<endl
<<"Imaginary = "<<c.imag<<endl;
return s;
}
void main()
{
complex a(1.0f,2.0f);
cout<<a;
}

Here you will agree that using cout<<a is easier to read and understand than the
other a.display().Here the << operator is overloaded ; the compiler interprets
the cout statement like this:
cout << ( operator <<(cout,a) );
Here if you notice, the real and imag data types are private data members of the
complex class but they are being accessed in the overloaded operator function.
Shouldn't the compiler complain? No, because the declaration of the overloaded
operator has been done inside the class and it has been preceded by a "friend"
keyword. Whenever a friend keyword is used it means that the specified function
( or class...as we see later ) can access the private members of the class it is
declared in. Hence the overloaded operator function is a friend of the complex
class ie. it can access all the private data members of the complex class.
Actually if you see, this violates the rules and concepts of Object Oriented
Programming. The Rules clearly state that an independant class has it's own
private data members and they should not be be accessible to any other class
except to those classes which are inherited from the base class.You will be able
to understand this a lot better when you reach the later half of this tutorial
in PART 2.
VI. THE COPY CONSTRUCTOR
Previously we saw a two step assignment technique which looked like this:
complex c;
c = a + b;
Where,
a and b are complex objects.
This used the assignment overloaded operator function.But sometimes we might
want to set a value to an object as soon as it is instantiated. It means that
our two step assignment technique becomes a one step process likt this:
complex c = a + b;
This can be made possible by using a special constructor known as the copy
constructor.Let us see how this can be done.
#include <iostream.h>
#include <conio.h>
class complex
{
private:
float real;
float imag;
public:
complex(float r=0.0f,float i=0.0f)
{
real=r;
imag=i;
}
complex(complex &c)
{
real = c.real;
imag = c.imag;
}
complex operator +(complex c)
{
complex t;
t.real = real + c.real;
t.imag = imag + c.imag;
return t;
}
// Other Functions
friend ostream& operator <<(ostream& s,complex c);
};
ostream& operator <<(ostream &s,complex c)
{
s<<"Real = "<<c.real<<endl
<<"Imaginary = "<<c.imag<<endl;
return s;
}

void main()
{
complex a(1.0f,2.0f),b(3.0,4.0);
complex c = a + b;
cout<<c;
}
A Copy Constructor is a constructor which takes another object of it's own class
as a parameter. Hence in the statement complex c = a + b; The compiler
interprets it like this:
complex &c(a.operator+(b));
All this time whenever i showed the interpretation of the compiler in overloaded
functions I forgot to mention that you can even use statements like that in our
programs. So in this example instead of "complex c=a+b;" we can use this as well
:
complex &c(a.operator+(b));
Be very careful while writing a copy constructor. You might notice that the
parameters received are passed by reference.The reason is quite simple. If an
object is passed by value another object would have to be created.The Copy
Constructor would then be called again, which means that the program would be
stuck in an infinite loop.But nowadays most compilers detect when a copy
constructor argument is passed by value and report it. But still we must be
careful since a lot of free compilers still don't report such errors.
VII. OVERLOADING OPERATORS.II
Earlier we saw how we can overload binary operators such as +,-,*,/ etc. Now if
we just wanted to increment or decrement an object we can overload that as well.
There is however a slight difference. Overloading unary operators are of two
types namely prefix and postfix operations. The prefix method increments/
decrements the object before the expression is evaluated while the postfix
method is quite just the opposite. Let's see how each one is done.Here's the
prefix method first:
#include <iostream.h>
class sample
{
private:
int data;
public:
sample()
{
data=0;
}
void display()
{
cout<<"data="<<data<<endl;
}
void operator++()
{
data++;
}
};
void main()
{
sample a;
a.display();
++a;
++a;
++a;
a.display();
}
The output here is:
data=0
data=3
Here if you see the syntax for overloading the prefix operator is the same like
overloading any other operator. You can use the prefix operator in places where
the multiplication operator ( or any other operator ) is overloaded. In such a
case the increment is done before the operation is completed. Now comes the
question of overloading the post-increment operator. The syntax for overloading
a post-increment operator is a bit different and is as follows:
void operator++(int)
{
data++;
}
The int here has got no special significance here. It is just for the compiler
to know that we are overloading the postfix operator. the postfix operator can
be used in the same way as we did in this example.
VIII. THE this POINTER
Remember the Complex Class Program we wrote earlier? It's multiplication
operator overloaded function was like this:
complex operator *(complex c)
{
complex tmp;
tmp.real = ( real * c.real ) - ( imag * c.imag );
tmp.imag = ( real * c.imag ) - ( imag * c.real );
return tmp;
}
Now while just quickly reading we may wonder what data type real is. Of course
it seems obvious here but while developing large applications, such confusions
are common. So to avoid such a confusion we have a "this" pointer that points to
itself. So let us by using the this pointer make a change in the previous
overloaded function.
complex operator *(complex c)
{
complex tmp;
tmp.real = ( this->real * c.real ) - ( this->imag * c.imag );
tmp.imag = ( this->real * c.imag ) - ( this->imag * c.real );
return tmp;
}
Isn't it a lot more readable now? this->real stands for the real data type
present in the current class. The this pointer can also be used for assignment
and displaying data. Have a look at this program to see how this is done:
#include <iostream.h>
class sample
{
private:
int data;
public:
sample()
{
data=0;
}
void display()
{
cout<<"data="<<data<<endl;
cout<<"this->data="<<data<<endl; // Another way of outputting data
cout<<"object's address="<<this<<endl; // getting address of object
}
void set(int item)
{
this->data=item; // Another way of assigning data
cout<<"object's address="<<this<<endl; // Getting address of object
}
};
void main()
{
sample a;
a.set(3);
a.display();
}
The Result on my PC was:
object's address=0x0012ff88
data=3
this->data=3
object's address=0x0012ff88
The object's address may vary from computer to computer. The Program along with
the comments seems self-explanatory.
The next example shall however show the real use of the this pointer in day to
day applications. Here is the Program:
#include <iostream.h>
class circle
{
private:
int radius;
float x,y;
public:
circle()
{
}
circle(int r,float x1,float y1)
{
radius=r;
x=x1;
y=y1;
}
circle operator=(circle c)
{
cout<<"*Assignment operator invoked*"<<endl;
radius=c.radius;
x=c.x;
y=c.y;
return circle(radius,x,y);
}
void display()
{
cout<<"Radius="<<radius<<endl
<<"X-Coordinate="<<x<<endl
<<"Y-Coordinate="<<y<<endl<<endl;
}
};
void main()
{
circle c1(20,5.5,5.5),c2,c3;
c3=c2=c1;
c1.display();
c2.display();
c3.display();
}
Most of the program is straightforward. The important thing here is that the
operator = is overloaded. This overloaded dunction is called when the statement
c3=c2=c1 gets executed. The function does all the copying of the member data
from one object to the other.It returns a value by creating a temporary circle
object and initialising it using the three argument constructor. The value
returned is a copy of the object of which the overloaded operator function is a
member. Returning a value makes it possible to chain = operators such as in like
c3=c2=c1. But returning by value creates an extra copy of the object leading to
wastage of space. When an object is returned by reference no new object is
created. Hence we can return the object by reference using the this pointer as
we now return a reference of the current object itself. Now the Assignment
operator function can be modified as follows:
circle operator=(circle c)
{
cout<<"*Assignment operator invoked*"<<endl;
radius=c.radius;
x=c.x;
y=c.y;
return *this;
}
Since this is a pointer to the object of which the above function is a member,
*this is that object itself. So the statement return *this returns this object
by reference.
IX. DECLARING DATA MEMBERS OUTSIDE THE CLASS
All this time, we were writing all our class functions within the class body
itself. Though this is acceptable, it is not practical in the longer run as it
affects readability when there are too many functions that make the class large.
So from now onwards, we will follow a new approach to writing classes. It's
simple, we just place the function prototypes in the class body and write the
function body outside the class. This way we can find out exactly how many
functions exist in a class and the class looks more neat. Even adding new
functions to the class gets simpler. Look at this example:
#include <iostream.h>
class sample
{
private:
int data;
public:
sample();
void display();
void func1();
~sample();
};
sample::sample()
{
cout<<"In Constructor\n";
}
void sample::display()
{
cout<<"In display()\n";
func1();
}
void sample::func1()
{
cout<<"In func1()\n";
}
sample::~sample()
{
cout<<"In Destructor\n";
}
void main()
{
sample s;
s.display();
}
The Class Body half of this program is simple. It just contains the function
prototypes of the functions that form a part of the class. What is important
here, is the function declaration outiside the class. Let us see the display
function declaration ie. void sample::display()
The '::' as most of you all know is the access modifier. Here it has to be
interpreted like this:
-> Function display of class sample that doesnt return a value ie. void
Same case here with constructors and destructors. Since neither of the two
return any values, only the latter half of the function declaration is used.
As you can see, in larger classes this method improves readability remarkably.
So as I said earlier, we will be using this method of writing classes from now
on.
X. The sizeof() Operator
Many Teachers blindly tell you that an integer occupies 2 bytes in memory. It's
time for you to know the truth. Not that they are wrong, but it's like they WERE
right, at some point of time. In 16-bit Processors, integers take up 2 bytes.
But in 32-bit processors, integers take up 4 solid bytes. Let's look at a
stronger evidence.
The sizeof() operator is used to find out how many bytes a particular data type,
class or structure takes up in memory. This is especially useful in binary File
Handling and for memory allocation where the size of the record or the data type
is not known. Meanwhile, study this example:
#include <iostream.h>
class fun1
{
};
class fun2
{
int i;
char c;
void function();
};
void main()
{
int intv;
cout<<sizeof(fun2)<<" bytes is size of class fun2\n"
<<sizeof(fun1)<<" bytes is size of class fun1\n"
<<sizeof(intv)<<" bytes is size of Integer\n\n"
<<sizeof(__int8)<<" bytes for 8-bit Integers\n"
<<sizeof(__int16)<<" bytes for 16-bit Integers\n"
<<sizeof(__int64)<<" bytes for 32-bit Integers\n"
<<sizeof(__int64)<<" bytes for 64-bit Integers\n";
}
Output:
5 bytes is size of class fun2
1 bytes is size of class fun1
4 bytes is size of Integer
1 bytes for 8-bit Integers
2 bytes for 16-bit Integers
8 bytes for 32-bit Integers
8 bytes for 64-bit Integers
Here we have to remember that the size of a class is determined only by it's
data members and not it's functions as functions are usually shared among each
class. Let us see how class fun2 occupies it's space:
1) class fun2: 5 Bytes
(char) + (int)
1 + 4
= 5
Notice the last half of output which shows the integer size for 8,16,32 and 64
bit respectively. Now you may have realised that the class fun1 has no data
members yet it's size is 1 and not 0. This is because if a class occupies 0
Bytes it is said to be non-existent. Hence the size of an empty class must be
the smallest positive non-zero number. Since 1 is the smallest positive non-zero
number each empty class occupies 1 Byte in memory.
XI. Structures v/s Classes
Basically, the purpose of both structures and classes is to group similar data
into one entity. Structures were designed for C while classes for C++.
In Theory, both can be used interchangeably. The only difference is that by
default, data members in structures are public while that in classes are private
.
Note that Structures in C cannot be extended like structures in C++. A Structure
in C cannot contain functions like that in C++ can.
Programmers generally use structures to group data together and classes to group
data and functions together.
CLASSES IN DETAIL
I. Static Data Members
We all know that each object has it's own copy of it's data members while the
member functions are shared among each object. However, when a data member is
declared as static only one copy of that data type is created for all objects
irrespective of the number of objects. It remains in scope till the end of the
program, which makes it like a usual static variable. Though data members are
used for operations on functions, static data members are meant for operations
on objects themselves. Let us see this example to clarify this point.
#include <iostream.h>

class sample
{
private:
int data;
static int objcount; // Declaration of objcount
public:
sample()
{
objcount++;
data++;
}
void display()
{
cout<<"data = "<<data<<endl
<<"objcount = "<<objcount<<endl<<endl;
}
};
int sample::objcount=0; // Definition of objcount
void main()
{
sample a,b,c;
a.display();
b.display();
c.display();
}
OUTPUT:
data = 2
objcount = 3
data = 4261613
objcount = 3
data = 4237585
objcount = 3
As you can see, as soon as an object is created the constructor increments both
the variables out of which objcount is static. Since 3 objects have been created
objcount holds the current number of objects of it's class. However the values
of data in all three cases are different ( 2, 4261613, 4237585 ) as each object
has it's own copy of the data variable. Since data has not been initialised it's
values are all junk.
Static data members require an unusual format. Ordinary Variables like data are
declared and defined in the same statemen. static data members like objcount,
requires two seperate statements. The variable's declaration is present within
it's class but it is defined outside the class.
The memory space for static data is allocated only once before the program
starts executing. A point to note: If you include the declaration of a static
variable but forget to write it's definition, the compiler would pass it but
the linker would complain that you're trying to reference an undeclared external
variable. This even happens if you include the definition but forget the class
name ie. the sample:: in the program above.
Lastly, the most important question: When would we require to use static member
data? Imagine a situation where an object is required to know how many objects
of it's class are in existence. In that case a similar program like the one
above can be used with the static member objcount that would be shared by all
objects.

Você também pode gostar