Owner: Learning Computer Programming URL:http://learning-computer-programming.blogspot.com Join Date: Tue, 12 Jun 2007 04:07:38 -0500 Rating:0 Site Description: I post articles, tips, tricks, techniques, algorithms etc. related to C++ Programming. Many of the things that I discuss also applies to other programming languages. Site statistics:Click here
Array of Functions 2007-07-28 01:09:00 In the article
Pointers to Function, we saw how pointers can be made to point
at functions and hence can be used to invoke them.
By far the most important use of pointers to functions is to have arrays of
functions. This can be achieved as stated below
You already know that we can have arrays of pointers and pointers can be made
to point at functions. So combining both we can have array of pointers to functions
put differently, we can have array of functions.
The example program below demonstrates how we can have array of functions;
please note that this concept is mostly used in writing compilers and interpreters,
so you shouldn’t expect the program to do anything serious or useful!
// Program to demonstrate
// array of functions
#include<iostream.h>
// -- FUNCTION PROTOTYPES --
void func1();
void func2();
void func3();
void func4();
void func5();
// -- ENDS --
void main()
{
// notice the prototype
void (*ptr[5])();
Read more:Array
, Functions
String Manipulation Functions (string.h) II 2007-07-27 08:26:00 This is the continuation of the article String
ManipulationFunctions
(string.h) in which we’re discussing about
the string manipulation functions.
Here I have listed 8 functions along with their prototype (simplified) and
a short description.
One thing to note here is that unlike the last article on this topic, I have
not included example programs, since the functions (with their prototypes) are
pretty much straightforward.
strlwr:
Prototype: char *strlwr(char *)
This function converts the given string to lowercase and returns the same.
strupr:
Prototype: char *strupr(char *)
This function converts the given string to UPPERCASE and returns it.
strncat:
Prototype: strncat(char *str1, const char *str2, int n)
It appends first ‘n’ characters of str2 to the end of str1.
strncpy:
Prototype: int strncmp(char *str1, const char *str2, int n)
This function compares first ‘n’ characters of str1 with str2,
it returns 0 if
Introduction to Inheritance in C++ 2007-07-31 01:37:00 Inheritance in C++ is one of the major aspects of Object Oriented Programming
(OOP). It is the process by which one object can inherit or acquire the features
of another object.
In C++ class and structure can use inheritance. It means we can make one class
(known as derived class) to acquire or inherit the features of another class
(known as base class).
Base class has general features common to all the derived classes and derived
class (apart from having all the features of the base class) adds specific features.
This enables us to form a hierarchy of classes.
Ex. if we define a class ‘computer’ then it could serve as the
base class for defining other classes such as ‘laptops’, ‘desktops’
etc.. This is because as you know that laptops have all the features of computers
and so have desktops (and so should their classes) but they have their specific
features too. So, rather than defining all the features of such classes f Read more:Introduction
, Inheritance
How String Functions Work? Part II 2007-07-30 01:57:00 This is the second part of the article How
StringFunctions
(strinh.h) Work?
Here we'll be designing our own version of some other commonly used standard
library string manipulation function that we discussed in the article String
Manipulation Functions (string.h) II.
These will help increase your programming skills further.
mystrlwr():
// mystrlwr function
#include<iostream.h>
// -- FUNCTION PROTOTYPES --
char *mystrlwr(char *);
// -- ENDS --
void main()
{
char ch[]="C++ ProGramming123";
cout<<mystrlwr(ch);
}
char *mystrlwr(char *str)
{
char *temp;
temp=str;
while(*str!=' ')
{
// change only if its a
// UPPER case character
// intelligent enough not to
// temper with special
// symbols and numbers
if(*str>=65 && *str<=90)
*str+=32;
str++;
}
return temp;;
}
mystrupr():
// mystrupr function
#include<iostre
Problems Related to Class and Inheritance 2007-08-03 01:56:00 To make our ongoing discussion on Inheritance
a little more interesting, I
have listed some problems or questions here. They all are related to Classes
in general and Inheritance in particular.
Problem #1: This program contains
some error(s), can you spot which line(s) contains error(s)?
1 // Problems No.1
2 // Related to Inheritance
3 #include<iostream.h>
4
5 // base class
6 class base
7 {
8 int a;
9
10 public:
11 void seta(int num){a=num;}
12 };
13
14 // derived class
15 class derived:public base
16 {
17 int b;
18
19 public:
20 void setb(int num){b=num;}
21
22 void show()
23 {
24 cout<<a<<"
"<<b;
25 }
26 };
27
28 // main
29 void main()
30 {
31 base b;
32 derived d;
33
34 b.seta(10);
35 d.setb(100);
36
37 d.show();
38 }
Problem #2:
This p
Parameterized Constructors of Derived Classes 2007-08-03 01:54:00 In the previous article Constructors
and Destructors of DerivedClasses
, we’re discussing about the
calling conventions of Constructors and Destructor functions of derived classes
whose base class also had them. There was one thing special about those constructors;
none of them were taking arguments.
If only the derived’s constructor takes parameters then also its O. K.
but what if both the base and derived class contains parameterized constructors
(as is obvious from the code below).
// this code contains ERRORS
// base class
class base
{
int a;
public:
base(int n)
{
//...
}
};
// derived class
class derived:public base
{
int b;
public:
derived(int m)
{
//...
}
};
//main
void main()
{
base b(10); //ok
derived d(10); // ERROR!!
// base's constructor
// will also be called and
// it needs parameters too !!
}
How’d you pass arguments to the bas
Constructors and Destructors of Derived Classes 2007-08-02 01:23:00 Classes can have Constructors and/or Destructors and Derived
Classes are no
different.
Situation remains understandable until both the base and its derived class
have Constructors and/or Destructors. Since the derived class contains more
than one Constructors and/or Destructors, it becomes confusing which one will
be called when.
This is because when an object the inherited class is constructed both the
constructors (base’s and its own) should be invoked and same applies when
it gets destructed.
This article will clear all this!
Consider the following example program:
// -- INHERITANCE --
// Constructors, Destructors
// and Inheritance
#include<iostream.h>
// base class
class base
{
public:
base(){cout<<"Constructing Base
";}
~base(){cout<<"Destructing Base
";}
};
// derived class
class derived:public base
{
public:
derived(){cout<<"Constructing Derived
";}
~derived(){cout<<"Destructing Derived
";}
};
Read more:Classes
Inheriting from Multiple Base Classes 2007-08-02 01:20:00 In the past articles we saw how we can make a class inherit from another class.
This way we could have a new class with features of the base class without explicitly
defining them. But what if, we want to have a new class with features form more
than one class. In other words, Is it possible for a derived class to inherit
multiple base classes (two or more).
Yes it’s possible!
The general from for deriving a class from multiple classes is:
class derived-class:access-specifier base1,access-specifier base2...
{
...
...
...
};
The following example program illustrates multiple inheritance practically:
// -- INHERITANCE --
// Program to illustrate multiple
// inheritance
#include<iostream.h>
// base class (1)
class base1
{
protected:
int a;
public:
void showa(){cout<<a<<"
";}
};
// base class (2)
class base2
{
protected:
int b;
public:
void showb(){cout<<b<<&qu Read more:Classes
, Multiple
Deriving a Class from another Derived Class 2007-08-01 08:49:00 Many of the peoples think that deriving a class from other derived classes
is a confusing thing, that’s why I have written this article to let them
know that deriving such classes is no different. To the compiler it doesn’t
matter whether the class from which a new class is derived is itself derived
or not.
The example program below illustrates this:
// -- INHERITANCE --
// Example program to illustrate
// the derivation of a class
// from another derived class
#include<iostream.h>
// base class
class one
{
int a;
public:
void setone(int num){a=num;}
int getone(){return a;}
};
// derived from base class 'one'
class two:public one
{
int b;
public:
void settwo(int num){b=num;}
int gettwo(){return b;}
};
// derived from derived class
// 'two'
class three:public two
{
int c;
public:
void setthree(int num){c=num;}
int getthree(){return c;}
};
void main(void)
{
// Read more:Derived
Protected Members of a Class 2007-08-01 08:45:00 In the previous article Defining
Base Class Acess (Inheritance) we noticed that when we derive one class
from a base class then no matter which base class access-specifier we use, the
members of the derived class doesn’t have access to the private members
of the base class.
But what if we want certain members of the base class to be private to the
class and at the same time accessible to the members of the derived class? Is
there any way of doing it?
Yes, it is possible, by declaring those members as protected members of the
base class.
The following example will show you how:
// ----------------------------
// -- THIS PROGRAM WON'T RUN --
// ----------------------------
// THIS PROGRAM IS DESIGNED TO
// HAVE ERRORS
#include<iostream.h>
// base class
class base
{
int a;
protected:
int b;
public:
int c;
};
// 'derived' class is
// inheriting 'base'
// publicly
class derived:public base
{
int d Read more:Members
Defining Base Class Acess (Inheritance) 2007-08-01 07:58:00 In the previous article Introduction
to Inheritance
in C++ we saw how one class can inherit properties and
functions from another, its general form is:
class derived-class:access-specifier base-class
{
...
...
...
};
We discussed that the access-specifier here, can be anyone of the three private,
public and protected. In this article we’ll discuss the meaning of each
of these in detail.
We know that when a class is derived from another class (base class) then the
members of the base class becomes the members of the derived class. The access-specifier
used while deriving the class specifies the access status of the base class
members inside the derived class.
If we don’t use any base class access-specifier then it’s taken
to be private by default.
Please note that in no case are the private members of the base class be accessible
to the members of its derived class.
So by using different access-specifier we only change t
Friend Functions of Class 2007-08-05 01:43:00 First, have a look at the following code:
// Using Friend Functions
#include <iostream.h>
class myclass
{
int a;
public:
friend int geta(myclass);
void seta(int x){a=x;}
};
// notice how the friend function
// can access even the private members
// of the class
int geta(myclass ob)
{
return ob.a;
}
void main()
{
myclass obj;
obj.seta(100);
// accessed as usual
cout<<geta(obj);
}
Did you notice the specialty?
In the above example program the function geta() is just a general function
(friend of class, of course) but still it is able to access the private member
of the class (i.e. the variable ‘a’). The function is also called
as usual since it is not a member function.
This is because by declaring any non-member function as friend inside a class,
gives it access to the entire Private and Protected members of that class.
Many of you would be wondering what is gained by doing this
Static Member Functions of Class 2007-08-05 01:38:00 Much like the Static
Member
s, there also exist static member functions. Just as static members,
static functions can also be accessed independently of any specific object and
thus its primary use is to pre-initialize static members before creation of
any object.
The following program illustrates how static member functions are declared
and used:
// Static Member Functions
#include <iostream.h>
class myclass
{
// declare a
static int a;
public:
// static function
static void init(int x){a=x;}
int get(){return a;}
};
// define a
int myclass::a;
void main()
{
// static functions may
// be called independently
// using the class name
myclass::init(100);
myclass obj;
cout<<obj.get();
}
In the above example the static members function (init() ) is used to initialize
the static member variable ‘a’ before object creation.
A few points to remember:
Static member function
Using Static Data Members in Classes 2007-08-04 09:13:00 Let us start with a question.
Suppose we want to have some information (i.e. a variable) which should be
available ‘as is’ to all the objects of a particular class (ex.
the number of objects of that class available at a time). Then what would you
do? You can’t make it to be a regular member of the class because then,
every object would have its own copy of that information which should have been
common to all objects.
One way of achieving this is to declare that variable as global and access
it within the class wherever needed as a member. This is illustrated in the
program below; it keeps track of the number of objects present (defined) at
a particular time.
#include <iostream.h>
int obj_count=0;
class myclass
{
public:
myclass(){obj_count++;}
~myclass(){obj_count--;}
int count(){return obj_count;}
};
void main(void)
{
myclass o1;
cout<<o1.count();
cout<<endl;
myclass o2;
Read more:Members
, Classes
Using Virtual Base Classes to Avoid Ambiguity 2007-08-04 09:10:00 Let’s start this with the following example program:
// this program contains errors
#include<iostream.h>
// base class
class base
{
public:
int a;
};
// derived class (1)
class derived1:public base
{
public:
int b;
};
// derived class (2)
class derived2:public base
{
public:
int c;
};
// derived class (3)
class derived3:public derived1, public derived2
{
public:
int d;
};
// main
void main()
{
derived3 ob;
// this is ambiguous
// since two copies of
// base is present in
// the class derived3
// one from derived1 &
// the other from derived2
ob.a=10;
}
The program above contains an error (as stated) in the following line:
ob.a=10;
Since the base class is inherited by the two classes (derived1 and derived2),
which are again inherited (both together) by another class derived3, there are
two copies of the base class (and hence two copies of variable ‘ Read more:Classes
, Avoid
Properties of Virtual Functions 2007-08-08 08:31:00 From the previous two articles Introduction
to Virtual Functions
and Virtual
Functions and Run-time Polymorphism, we have been discussing about
Virtual Functions.
In this article we’ll be discussing about two important properties of
Virtual Functions.
As properties can be better understood by examples, we’ll be using them
more rather than text and definitions that could confuse you.
Property #1:
// Properties
of virtual functions
#include <iostream.h>
// base class
class base
{
public:
virtual void func()
{
cout<<"Base's func()
";
}
};
// derived class
class derived1:public base
{
public:
// this is a virtual function
void func()
{
cout<<"Derived1's func()
";
}
};
// derived from another
// derived class
class derived2:public derived1
{
public:
// still virtual
void func()
{
cout<<"Derived2's func()
";
Virtual Functions and Run-time Polymorphism 2007-08-06 08:41:00 Before beginning this I would like to tell you one thing through the following
program:
// Virtual Functions
and
// Run-time Polymorphism
#include <iostream.h>
// base class
class base
{
public:
int a;
};
// derived class
class derived:public base
{
public:
int b;
};
// main
void main()
{
base b;
derived d;
// base class pointer
base *bptr;
// pointer pointing
// to base's object
bptr=&b;
bptr->a=10;
// pointer pointing
// to derived's object
bptr=&d;
// still is able to access
// the members of the base
// class
bptr->a=100;
}
The property above combined with virtual function can be used to achieve a
very special and powerful feature, known as run-time polymorphism.
We had discussed about What
is Polymorphism before so we wont be discussing it here.
The program below illustrates how virtual functions can be used to achieve
run-time polymo
Introduction to Virtual Functions 2007-08-06 08:38:00 Virtual functions are special member functions of a class which may be re-defined
in the derived classes. It is used to give specific meaning to the base class
member function with respect to the derive class.
Virtual functions can be thought of as a function name reserved in the bas
class which may be re-defined in the derived classes as per the need so that
every derived class has the same function that performs specific (as redefined
in the derived class) action.
Let’s now have a look at a simple program to show virtual functions inaction:
// Virtual functions
#include <iostream.h>
// base class
class base
{
public:
// precede the function name
// with the 'virtual' keyword
// to make it a virtual function
virtual void func()
{
cout<<"Base's func()
";
}
};
// derived class
class derived:public base
{
public:
// redefinition of the
// function
void func()
{
cou Read more:Functions
, Introduction
Properties of Pure Virtual Functions 2007-08-11 08:51:00 In the article Properties
of Virtual Functions
, we discussed about two properties of virtual
functions in detail. In this article we’ll be discussing about some properties
of Pure
Virtual Functions.
Property #1: We know that a
base class can’t define a pure virtual function and at the same time its
derived class must define it. But what if the derived class is used as base
for deriving yet another class; is this possible?
Yes, it is, as is obvious from the following program:
#include <iostream.h>
// base class
class base
{
public:
// pure virtual function
// declaration
virtual void func() = 0;
};
// derived class
class derived1 : public base
{
public:
// must define
void func()
{
cout<<"Derived1's func()
";
}
};
// derived from class derived1
class derived2 : public derived1
{
public:
void func()
{
cout<<"Derived2's func()
";
Practical Example of Using Virtual Functions 2007-08-10 08:35:00 From the past few articles we have been discussing about virtual functions
but we are yet to observe any of its practical use, this article would do that!
In this article we are going to show you a very simple program that illustrates
the practical use of virtual functions.
Please read the code carefully!
// Practical
example of
// when virtual functions are
// used
#include <iostream.h>
class area
{
protected:
int mag;
double a;
public:
area(int x){mag=x;}
double get_area(){return a;}
// pure virtual function
virtual void compute()=0;
// it is made pure as
// it couldn't have any meaningful
// definition since area can only
// be defined w.r.t something specific
};
class circle_area : public area
{
public:
circle_area(int x) : area(x){}
// now that we are referring
// to area w.r.t a circle so
// it is natural that we define
// it
void compute()
{
a=(mag*mag)*3.14;
Read more:Functions
, Example
Pure Virtual Functions 2007-08-10 08:30:00 From the previous article Properties
of Virtual Functions
, we know that a virtual function may or may not
be overridden in the derived lasses. It means, it is not necessary for a derived
class to override a virtual function.
But there are times when a base class is not able to define anything meaningful
for the virtual function in that case every derived class must provide its own
definition of the that function. To force this type of overriding you use the
following general form to declare a virtual function:
virtual ret-type func-name(arg-list)=0;
This type of virtual function is known as Pure Virtual Function.
There are two major differences between a virtual and a pure virtual function,
these are below:
There CAN’T be a definition of the pure virtual function in the base
class.
There MUST be a definition of the pure virtual function in the derived
class.
By making a virtual function ‘Pure’, it becomes n
Introduction to Operator Overloading in C++ II 2007-08-13 08:16:00 From the previous article Introduction
to Operator
Overloading in C++, we know that we can overload operators
by defining member functions having special names. The general form, as you
know, for overloading operators is:
ret-type operator#(arg-list);
Although, you’re free to use any return type as ret-type but commonly
it’ll be the name of the class itself; in this article we’ll be
closely examining why is it so and what would happen if we use other return
types.
You may not expect further theories in this article as all the further discussion
is in the program as comments.
I request you to read the program thoroughly so you may understand what we’re
discussing.
// Example program to illustrate
// operator overloading with diff rent
// return-types used in the overload
// function
#include <iostream.h>
// class
class myclass
{
int a;
public:
// default constructor
myclass(){}
// constructor
Introduction to Operator Overloading in C++ 2007-08-13 02:23:00
a1 = a2 + a3;
The above operation is valid, as you know if a1, a2 and a3 are instances of
in-built Data
Types. But what if those are, say objects of a Class;
is the operation valid?
Yes, it is, if you overload the ‘+’ Operator
in the class, to which a1, a2 and a3 belong.
Operator overloading is used to give special meaning to the commonly used operators
(such as +, -, * etc.) with respect to a class. By overloading operators, we
can control or define how an operator should operate on data with respect to
a class.
Operators are overloaded in c++ by creating operator functions either as a
member or a s a Friend Function of a class. Since creating member operator functions
are easier, we’ll be using that method in this article.
As I said operator functions are declared using the following general form:
ret-type operator#(arg-list);
and then defining it as a normal member function.
Here, ret-type is commonly the name of the class itself a Read more:Introduction
Practical Example of Using Virtual Functions II 2007-08-13 02:16:00 From the past few articles we have been discussing about Virtual
Functions
. Before taking up another topic for discussion I thought
of providing one more example of how and when virtual functions may be used.
So, here it is, a practical
example of virtual function.
As virtual functions and Run-Time
Polymorphism goes hand-in-hand so the example here may also serve as
an example of the use of run-time polymorphism.
// Example
to illustrate
// the use of virtual functions
// and run-time polymorphism
#include <iostream.h>
// -- SORT CLASS --
class sort
{
protected:
int *arr;
int num_elmnt;
public:
sort(int);
~sort();
void get_elmnt();
void show_elmnt();
virtual void do_sorting()=0;
};
// takes an argument
// which is the number of
// elements we want
sort::sort(int x)
{
num_elmnt=x;
arr=new int[num_elmnt];
}
sort::~sort()
{
// free up he allocated memory
delete []arr;
}
void Read more:Practical
Operator Overloading using Friend Functions 2007-08-18 02:55:00 In the article Introduction
to Operator
Overloading in C++, we discussed that there are two methods
by which operators can be overloaded, one using the member function and the
other by using friend
functions.
There are some differences between the two methods though, as well as there
are advantages for using friend functions to overload operators over member
functions.
In this article we’ll be overloading the simplest operators – and
+ using friend function. Previously we have seen that we need to accept only
one argument explicitly for binary operators and the other is passed implicitly
using the ‘this’
pointer.
From the article Friend
Functions
of a Class, we know that as friend functions are not members
of a class, they don’t have a ‘this’ pointer. So how the operands
are are passed in this case?
Simple, all the operands are passed explicitly to the friend operator functions.
There are other differences
Problems Related to Operator Overloading 2007-08-18 02:53:00 To make our ongoing discussion on Operator
Overloading more interesting, here
I have listed some problems related to operator overloading.
Problem #1: Point out the errors(s)
if any, in the following program:
1 // Problem related to Operator
2 // overloading
3 #include <iostream.h>
4
5 class myclass
6 {
7 int a;
8
9 public:
10 myclass(int);
11 void show();
12
13 myclass operator ++();
14 myclass operator --();
15 };
16
17 myclass::myclass(int x)
18 {
19 a=x;
20 }
21
22 void myclass::show()
23 {
24 cout<<a<<endl;
25 }
26
27 myclass myclass::operator ++()
28 {
29 a++;
30
31 return *this;
32 }
33
34 myclass myclass::operator --()
35 {
36 a--;
37
38 return *this;
39 }
40
41 // main
42 void main()
43 {
44 myclass ob(10);
45
46 ob.show();
47
48 ob++;
49 ob.show();
50 }
Problem #2: Point out the errors(s)
if any, i
Class with all the Overloaded Arithmetic Operators 2007-08-17 08:24:00 So far we have learnt to overload +, -, +=, -= etc. operators and we know what
is the basic theory behind operator overloading.
In this article we are going to design a program with a class that overloads
almost all the arithmetic operators (+, -, +=, -=, /, *, ++, --)
This is a program centric article, so we straightway have a look at the example
program.
Since nothing new has been introduced, I leave it up to you to understand everything
yourself.
// Example Program with a
// a class having almost
// all the arithmetic
// operators overloaded
#include <iostream.h>
class myclass
{
int a;
int b;
public:
myclass(){}
myclass(int,int);
void show();
myclass operator+(myclass);
myclass operator-(myclass);
// prefix
myclass operator++();
myclass operator--();
// postfix
myclass operator++(int);
myclass operator--(int);
myclass operator+=(myclass);
myclass operator-=(myclass);
myclass ope
Overloading the Short-Hand Operators (+= and -=) 2007-08-17 08:22:00 The short-hand addition (+=) and subtraction (-=) operators are very commonly
used and thus it would be nice if we overload them in classes. You would be
glad to know that there is nothing new or special that needs to be done to achieve
this, both the operators are overloaded as usual like other binary operators.
The short-hand operators combine the operation performed by two operators one
the addition or subtraction and the other the assignment. This is all it does
and this is all you need to know!
As nothing new has been introduced so we end the theory here and look at the
example:
// Program to illustrate the
// overloading of shorthand
// operators (+=, -=)
#include <iostream.h>
class myclass
{
int a;
int b;
public:
myclass(int,int);
void show();
myclass operator+=(myclass);
myclass operator-=(myclass);
};
myclass::myclass(int x,int y)
{
a=x;
b=y;
};
void myclass::show()
{
cout<<a<& Read more:Short
Overloading the Assignment Operator (=) 2007-08-15 08:44:00 We know that if we want objects of a class to be operated by common operators
then we need to overload
them. But there is one operator whose operation is automatically crested
by C++ for every class we define, it is the assignment operator ‘=’.
Actually we have been using similar statements like the one below previously
ob1=ob2;
where ob1 and ob2 are objects of a class.
This is because even if we don’t overload the ‘=’ operator,
the above statement is valid.
As I said C++ automatically creates a default assignment operator. The default
operator created, does a member-by-member copy, but if we want to do something
specific we may overload it.
The simple program below illustrates how it can be done. Here we are defining
two similar classes, one with the default assignment operator (created automatically)
and the other with the overloaded one. Notice how we could control the way assignments
are done in that case.
// Program to Read more:Operator
, Assignment
Overloading Post-Fix Forms of ++ and -- Operators 2007-08-15 08:40:00 In the article Overloading
Increment/Decrement Operators, we overloaded the prefix from of the
increment and decrement operators. For that we defined the operator function
with the following general form:
ret-type operator++();
ret-type operator--();
As there are two-two forms (prefix, postfix) of each of these operators while
operator
function related with each can be only one, so to C++ has devised a
way to distinguish between the two forms.
The operator++() or operator—() functions are called as usual when prefix
form of operators are used but when postfix form is used then an integer argument
0 is passed to that function.
To catch those calls we must overload one more function for each, accepting
an integer as argument as below:
ret-type operator++(int);
ret-type operator--(int);
We don’t need to use the argument (0) passed since it is passed just
because we can define two overloaded
functions for each operator and the compil Read more:Forms