Post Your Wish

Sunday, April 10, 2011

Tutorial C++


C++ Classes

In this C++ programming tutorial we take a look at classes. A class is a mechanism for creating user-defined data types. It is similar to the C language structure data type.
A class can not only hold data variables, but can also hold functions. These variables and functions are members of a class. The variables are called data members and functions are called member functions.
In C++, a class type can be declared with the keywords union, struct, or class. A union object can hold any one of a set of named members. Structure and class objects hold a complete set of members. Each class type represents a unique set of class members including data members, member functions, and other type names. The default access for members depends on the class key:
  • The members of a class declared with the keyword class are private by default. (A class is inherited privately by default.)
  • The members of a class declared with the keyword struct are public by default. (A structure is inherited publicly by default.)
  • The members of a union (declared with the keyword union) are public by default. (A union cannot be used as a base class in derivation.)
The keyword class is generally used when declaring a class type.
The general form of a class looks like this:

 class class_name
 {
  access_specifier:
   member1;
  access_specifier:
   member2;
 };

As you can see it looks very similar to the declaration of structures. (Difference with a structure is that we now can include functions and members). Class_name is the identifier for the class.
The access_specifier can be one of the following keywords: private, public or protected. With these specifiers the access right of the members that will follow are set.
  • private members of a class are only accessible by other private members of the same class.
  • protected members are accessible by members of the same class.
  • protected members are also accessible by members of derived classes.
  • public members are accessible from anywhere where the object is used.
Note: all members of a class (declared with the class keyword) have private access by default, unless another specifier overrules it.
Take a look at the following example:

 class CAdd
 {
   int x , y;
  public:
   int add(int,int);
 };

Note: C before Add is used to indicate that it is a class.
In the example above the integers x and y are private members. The function add() is public. To be clear it is not necessary to use the private specifier, but to be clear you better use it. Then the example will become:

 class CAdd
 {
  private:
   int x , y;
  public:
   int add(int,int);
 };

Member functions can access the private/protected data members of their class and manipulate them. No external functions can access the private/protected data members of a class.
Let’s look at a complete example:

 #include <iostream>
 using namespace std;

 class CAdd
 {
  private:
   int x, y;
  public:
   void add(int,int);
   int ret() { return x+y; }
 };

 void CAdd::add(int a, int b)
 {
  x = a;
  y = b;
 }

 int main()
 {
  CAdd my_object;

  my_object.add(4,4);
  cout << my_object.ret() << endl;

  return 0;
 }

So let’s look at the program we created. In the class CAdd we declared two private members of the type integer with the name x and y. We also declared two functions (both are public members): add() and ret().
In the function main() we declared the object my_object. To access the functions, which are defined in the class, we can use the object followed by a dot (.) and then the name of the member (function). For instance: my_object.add(4,4); (the same as we did with structures).
You may notice the difference between the two functions add() and ret(). The body of the function ret() is filled. For the function add() there is only a prototype defined. As you can see we defined the member function add() outside the class declaration:

 void CAdd::add(int a, int b)
 {
  x = a;
  y = b;
 }

In the code above we say that function add() is member of the class CAdd by putting the operator of scope (two colons (::)) between them. By using the scope operator we getting access to other members in the class. So we can use the integers x and y. (This are private members thus only accessible by members of the class.)
The only difference between defining a class member function completely within its class and to include only the prototype (later on its definition), is that in the first case the function will automatically be considered an in-line member function by the compiler, while in the second it will be a normal (not in-line) class member function, which in fact supposes no difference in behavior.
A great advantage of a class is that we can declare several objects of it. Note: when using different objects each object will get its own variables x and y. So each object will get its own variables to operate with.

_______________________________________________________________



C++ Exceptions and exception handling

Exceptions are situations which must be avoided during program executions. Exceptions are caused by errors, invalid inputs or invalid processing. Exceptions can lead to either program termination or generating unexpected outputs.
In general, it is assumed that exceptions are errors but this is not always true. We can state:
All errors are exceptions but not necessarily all exceptions are errors.
Exceptions can be handled or avoided by a simple control statement such as an if-else statement, but most languages provide a separate mechanism of exception handling.

Exception Handling

Exception handling is a process of handling exceptional situations that may occur in a program due to the above stated reasons in such a way that:
  • The program will terminate gracefully i.e. it will give a proper message and then will terminate the program.
  • After giving the proper message stating the reason of the exception the program continues to execute after correcting the error.
In the C++ language, exception handling is performed using the following keywords:
  • try
  • catch
  • throw
  • throws
Take a look at the process of exception handling:
To catch exceptions we must place a portion of code under exception inspection. We can do this by putting that portion of code in a try block. So, when an exceptional circumstance arises (within that block) an exception is thrown. This in turn transfers the control to the exception handler. If there are no exceptions thrown, the code will continue normally and all handlers are ignored.
Take a look at an example:

 #include<iostream>
 using namespace std;

 int main ()
 {
  try
  {
   throw 5;
  }

  catch (int a)
  {
   cout << "An exception occurred!" << endl;
   cout << "Exception number is: " << a << endl;
  }
  return 0;
 }

As you can see, the code under exception handling is enclosed in a try block. We simply throw an exception with the statement: throw 5;
The throw expression can accept on parameter, which is passed as an argument to the exception handler.
As you can see, in this example we passed the integer value five.
We declared the exception handler with the catch keyword. As you can see, the catch format looks the same as a normal function. The catch statement always has at least one parameter.
The type of the parameter used in the catch statement must be the same as the type used in the throw statement. If it is not, the exception is not caught. For this reason it is possible to use multiple exception handlers. We just use different catch statements which in turn use different parameter types. Only the handler that matches its type with the argument specified in the throw statement is executed.
Let’s take a look at such an example:

 #include<iostream>
 #include <string>
 using namespace std;

 int main ()
 {
  int num;
  string str_bad = "wrong number used";
  cout << "Input 1 or 2: ";
  cin >> num;

  try
  {
   if ( num == 1 )
   {
    throw 5;
   }
   if ( num == 2 )
   {
    throw 1.1f;
   }
   if ( num != 1 || num != 2 )
   {
    throw str_bad;
   }
  }

  catch (int a)
  {
   cout << "An exception occurred!" << endl;
   cout << "Exception number is: " << a << endl;
  }
  catch (float b)
  {
   cout << "An exception occurred!" << endl;
   cout << "Exception number is: " << b << endl;
  }
  catch (...)
  {
   cout << "A default exception occurred!" << endl;
   cout << "Why? : " << str_bad << endl;
  }
  return 0;
 }

The program above will throw an exception after you input something. If the number is a 1 then an integer is thrown. If the input is a 2 then a float is thrown. If it is neither of these two (not an integer or float) the default exception handler is used. This default exception handler uses the ellipsis (…) as the parameter of catch. That handler will catch any exception no matter what the type of the throw exception is. (In this case a string is used.)
It is possible to nest try-catch blocks, but you have to test it very well because it is easy to make a mistake which in turn can lead to an unexpected result.

Exception and functions

If we declare a function, we can limit the exception type that the function might throw. We can do this by adding a throw suffix to the function declaration. For example:

int a_function (int param) throw(int);

int a_function (int param) throw();

int a_function (int param);

The first function of the examples above takes one argument of the type integer and will return an integer.
The only exception that this function might throw is an exception of type int.
The second function also takes one argument of the type integer and will return an integer. The function may not throw exceptions, because there is no type specified between the round brackets. (No type specified means that the function is not allowed to throw exception.)
The last function may throw exception of any type.

Standard exceptions

The C++ standard library provides a base class specifically designed to declare objects to be thrown as exceptions. To make use of the standard exceptions we have to include the exception header file.
All of the exceptions thrown by parts of the C++ Standard library will throw exceptions derived from the std::exception class. These exceptions are:
bad_alloc
A bad_alloc is thrown by new if an allocation failure occurs.
bad_cast
A bad_cast is thrown by dynamic_cast when it fails with a referenced type.
bad_exception
A bad_exception is thrown when an exception type doesn’t match any catch
bad_typeid
A bad_typeid is thrown by typeid
ios_base::failure
An ios_base::failure is thrown by functions in the iostream library.
So let’s use it in an example, but be warned this example can lock your computer! It should not happen, but it might.

#include<iostream>
#include <exception>
using namespace std;

int main()
{

 try
 {
  int * my_array1= new int[100000000];
  int * my_array2= new int[100000000];
  //int * my_array3= new int[100000000];
  //int * my_array4= new int[100000000];
  //add more if needed.
 }

 catch (bad_alloc&)
 {
  cout << "Error allocating the requested memory." << endl;
 }

 return 0;
}

Note: you should add more my_array’s until exception occurs. You could also add some free statements in the catch block, freeing up already claimed memory.
It is recommended to use try blocks if you use dynamic memory allocation. This way you are in control if an allocation fails. For example: you unload all used resources and then end the program.
__________________________________________________________


C++ Preprocessor Directives

In this C++ programming tutorial we will look at compiling and preprocessor directives. Whenever a CPP program is compiled then the following steps are taken:
Preprocessors
The pre-processor is a utility program, which processes special instructions that can be or are written in a C/CPP program. These instructions can be include a library or some special instructions to the compiler about some certain terms used in the program.
Pre-processor directives start with ‘#’ character.
Different preprocessor directives (commands) perform different tasks. We can categorize the Preprocessor Directives as follows:
  • Inclusion Directives
  • Macro Definition Directives
  • Conditional Compilation Directives
  • Other Directives

Inclusion Directive

This category has only one directive, which is called#include. This inclusion directive is used to include files into the current file. The inclusion directive can be used as follows:
#include <stdio.h>includes stdio.h from include
folder
#include <iostream>includes cpp class library header
iostream
#include<my.cpp>includes my.cpp file from include
folder
#include “my.h”includes my.h file from current working
folder
#include “myfolder/abc.h”includes abc.h file from the myfolder which
is available in current working folder
From the above usage-table we can see that #include is used with two options angular brackets (<>)and inverted commas (“”). When #include is written with <> it means we are instructing to include the files from the include path defined in the compiler settings which is generally the include folder from the compiler. When #include is written with “” it means we are instructing to include the a file: first the current folder is checked and if it is not found there, then the include folder of the compiler is checked.

Macro Definition Directives

These are used to define macros, which are one, or more program statements (like functions) and they are expanded inline. (Expanded inline means they are substituted at the place of macro call unlike functions in which the execution pointer moves to the function definition.).
There are two directives for Macro Definition:
  • #define – Used todefine a macro
  • #undef – Used toundefine a macro (The macro cannot be used after it is undefined.)
Take a look at the example, which is using include directive and some macro definition directives:

#include <iostream>
using namespace std;

#define PI 3.14

#define SQR(X) ((X)* (X))
#define CUBE(y) SQR(y)* (y) 

int main(int argc,char *argv[])
{
 int radius; 

 cout << endl << "Enter Radius of the Circle:"; cin >> radius;
 cout << endl << "Area of the Circle is: " << PI * SQR(radius); 

 #undef PI
 //It is now invalid to use PI, thus we cannot use the next line.
 //cout << endl << "Perimeter  of Circle is : " <<  2 * PI * radius;

 cout << endl << "Area of the Sphere of Radius " << \
  radius << " is: " << 4/3.0 * 3.14 * CUBE(radius);
}

The above example illustrates the following:
  • We do not terminate the macros using (;)
  • Macros can be parameterized
  • Macros can be dependent on other macros (must be defined before they are used.)
  • Macros are customarily written in capital letters to avoid confusion between macro and functions. (this is not a rule)
  • Parameters are enclosed in () to avoid the ambiguity arising due to operator precedence. For instance in the example above we call SQR(4+3). This statement will be expanded as (4+3) * (4+3).
  • Directives are helpful whenever things are prone to changes and are used at many places in one program. Changing the directive value at the top will cause the corresponding changes at all the places in the program.
If the directive requires use of more than one line then it can be continued in the next line after placing ‘\’ at the end of the first line.
Take a look at the following example:

 #include <iostream>
 using namespace std;

 #define greater(m,n)(m>n) ? (m) \
   : (n);

 int main(int argc, char *argv[])
 {
  int number1, number2;

  cout << endl << "Enter two numbers:";

  cin >> number1 >> number2;

  int g = greater(number1, number2);
  cout << endl << "Greater of the two numbers is: " << g;
 }

Conditional Compilation Directives

Conditional Compilation Directives these are used to execute statements conditionally for:
  • Executing the code on different machine architectures.
  • Debugging purposes.
  • Macros can be dependent on other macros (must be defined before they are used.)
  • Evaluating codes, which are to be executed depending upon the requirements of the programmer.
The following directives are included in this category:
  • #if
  • #elif
  • #endif
  • #ifdef
  • #ifndef
Note: These macros are evaluated on compile time. Therefore they can only use the predefined macros or literals. Most compilers do not support the use of variables with these directives.

#include <iostream>
using namespace std;

#define US 0
#define ENGLAND 1
#define NETHERLANDS 2

//We define Netherlands as country, then we get Euro.
//If we define England we get pounds and so on.
#define CURRENCY NETHERLANDS

int main(int argc, char *argv[])
{
 #if CURRENCY == US
  char acurrency[] = {"Dollar"};
  #define CHOSEN US
 #elif CURRENCY == ENGLAND
  char acurrency[] = { "Pound" };
  #define CHOSEN ENGLAND
 #elif CURRENCY == NETHERLANDS
  char acurrency[] = { "Euro" };
  #define CHOSEN NETHERLANDS
 #else
  char acurrency[] = {"Euro"};
 #endif

 // If CHOSEN is not defined, then we get default.
 #ifndef CHOSEN
  cout << endl << "Using Default Currency " << acurrency;
 #else
  cout << endl << "Chosen Currency " << acurrency;
 #endif
}

Other Directives

The following directives are the “other” directives:
  • #error
  • #line
  • #pragma

#error

When the #error directive is found the program will be terminated and the message (that is given after the #error statement) will be used in the compilation error that is given by the compiler.
Take a look at an example:

 #ifndef __cplusplus
 #error Compilation Error: A C++ compiler is required to compile this program!
 #endif

The __plusplus macro is by default defined by most C++ compilers. So if this macro is not found, during compilation, then the compilation is aborted and the message (Compilation Error: A C++ compiler is required to compile this program!) is printed for the user.

#line

The #line directive is used to change the value of the __LINE__ and __FILE__ macros. (Note: the filename is optional.) The __LINE__ and __FILE__ macros represent the line being read and the file being read.

 #line 50 "myfile.cpp"

This will set the __LINE__ to 50 and __FILE__ to myfile.cpp

#pragma

The #pragma directive is used to allow suppression of specific error messages, manage heap and stack debugging, etc. These are compiler specific directives. (Check your compiler documentation for the #pragma lines you can use.)
For Example “#pragma inline” statement indicates the assembly code is available in the code.
If the directive is unavailable in an implementation the statement is ignored.

Predefined Macros

__LINE__

An integer that gives the line number of the current source file being compiled.

__FILE__

A string that gives the name of the source file being compiled.

__DATE__

A string that gives the date, in the form “Mmm dd yyyy”, when the compilation process began.

__TIME__

A string that gives the name of the source file being compiled.

__STDC__

Forces the use of standard C/CPP codes only in the program.

__cplusplus

Contains a 6 digit number to indicate that the compiler is based on the standards, otherwise it contains 5 or fewer digit
__________________________________________________________

C++ Tutorial – Namespaces and anonymous namespaces

Namespaces are used in the C++ programming language to create a separate region for a group of variables, functions and classes etc. Namespaces are needed because there can be many functions, variables for classes in one program and they can conflict with the existing names of variables, functions and classes. C++ uses namespace to avoid the conflicts.
The concept can be depicted using the following diagram:
namespaces
The following will list the primary reasons due to which a conflict can occur:
  • Between User defined variables/functions/classes and built in Library variables/functions/classes (e.g. sqrt(), abs() etc.)
  • Between the separately included library and built in library
  • Between the variables/functions/classes of the one separately included library and the same of the other separately included library

Creating Namespaces

The C++ language include the keyword “namespace” for creating namespaces.

 namespace
 {
  members of namespace;
 }

Take a look at an example:

#include<iostream>
using namespace std;

namespace myconstants {
 const double pi = 3.141592;
}

namespace myshapes {
 double area;
 double perimeter;

 void AreaOfCircle(double radius)
 {
  area = myconstants::pi * radius * radius;
 }

 void PerimeterOfCircle(double radius)
 {
  perimeter = 2 * myconstants::pi * radius;
 }
}

int main(int argc, char * argv[])
{
 double r;
 cout << endl << "Enter Radius:";
 cin >> r;
 myshapes::AreaOfCircle(r);
 cout << endl << "Area of the Circle is :" << myshapes::area;
 myshapes::PerimeterOfCircle(r);
 cout << endl << "Perimeter of the Circle is :" << myshapes::perimeter;
}

Namespaces have the following important points:
1) We can have more than one namespace of the same name. This gives the advantage of defining the same namespace in more than one file (although they can be created in the same file as well).
Take a look at an example:

 #include<iostream>
 using namespace std;

 namespace mynamespace
 {
  int x;
 }

 namespace mynamespace
 {
  int y;
 }

 int main(int argc, char * argv[])
 {
  mynamespace::x = mynamespace::y =5;
  cout << mynamespace::x << endl << mynamespace::y;
 }

2)We can have anonymous namespaces (namespace with no name). They are directly usable in the same program and are used for declaring unique identifiers. It also avoids making global static variable.
The “anonymous” namespace you have created will only be accessible within the file you created it in.
Take a look at an example:

 /*This is file1.cpp*/
 #include<iostream>
 using namespace std;

 namespace
 {
  int local;
 }

 void func();

 int main()
 {
  local = 1;
  cout << "Local=" << local << endl;
  func();
  cout << "Local=" << local << endl;
  return 0;
 }


 /* This is file2.cpp */

 namespace
 {
  // Should not collide with other files
  int local;
 }

 void func()
 {
  local = 2;
 }

Note: you will need to include both the files in the same project.
The result of this program should be:
Local = 1
Local = 1
It should not be:
Local = 1
Local = 2
3) C++ has a default namespace named std, which contains all the default library of the C++ included using #include directive.

Using Namespaces

Namespaces are used with the ‘using’ keyword, which makes all the members of the namespace available in the current program and the members can be used directly, without taking reference of the namespace.
Namespaces are also usable by taking their name reference with scope resolution operator. This method allows distinguishing between the members of the same name in two different namespaces.
Take a look at an example:

 #include<iostream>
 using namespace std;

 namespace mynamespace
 {
  int i,j;
 }

 using namespace mynamespace;

 int main(int argc, char *argv[])
 {
  cout << endl << i << ", " << j;
 }

Or without the ‘using’ keyword:

 #include<iostream>
 using namespace std;

 namespace mynamespace
 {
  int i,j;
 }

 int main(int argc, char *argv[])
 {
  std::cout << endl << mynamespace::i << ", " << mynamespace::j;
 }

Namespace alias

It is also possible to declare an alternate name for an existing namespace.
We can use the following format:

 namespace new_name = current_name;

Namespace std

All the files in the C++ standard library declare all of its entities within the std namespace. For that reason we include the ‘using namespace std;’ statement in most programs that is using any entity defined in iostream.
__________________________________________________________


C++ Typecasting Part 1

Typecasting is the concept of converting the value of one type into another type. For example, you might have a float that you need to use in a function that requires an integer.

Implicit conversion

Almost every compiler makes use of what is called automatic typecasting. It automatically converts one type into another type. If the compiler converts a type it will normally give a warning. For example this warning: conversion from ‘double’ to ‘int’, possible loss of data.
The problem with this is, that you get a warning (normally you want to compile without warnings and errors)
and you are not in control. With control we mean, you did not decide to convert to another type, the compiler did. Also the possible loss of data could be unwanted.

Explicit conversion

The C and C++ languages have ways to give you back control. This can be done with what is called an explicit conversion. Sure you may still lose data, but you decide when to convert to another type and you don’t get any compiler warnings.
Let’s take a look at an example that uses implicit and explicit conversion:

 #include <iostream>
 using namespace std;

 int main()
 {
  int a;
  double b=2.55;

  a = b;
  cout << a << endl;

  a = (int)b;
  cout << a << endl;

  a = int(b);
  cout << a << endl;
 }

Note: the output of all cout statements is 2.
The first conversion is an implicit conversion (the compiler decides.) As explained before, the compiler should give a warning.
The second conversion is an explicit typecast, in this case the C style explicit typecast.
The third conversion is also explicit typecast, in this case the C++ style explicit typecast.

Four typecast operators

The C++ language has four typecast operators:
  • static_cast
  • reinterpret_cast
  • const_cast
  • dynamic_cast

Static_cast

Automatic conversions are common in every C++ program. You have:
  • Standard conversion. For instance: from short to int or from int to float.
  • User defined conversions (Class conversions.)
  • Conversion from derived class to base class.
    (Take a look at the inheritance tutorial)
The static_cast can be used for all these types of conversion. Take a look at an example:

 int a = 5;
 int b = 2;
 double out;

 // typecast a to double
 out = static_cast<double>(a)/b;

It may take some time to get used to the notation of the typecast statement. (The rumour goes that Bjarne Stroustrup made it difficult on purpose, to discourage the use of typecasting.) Between the angle brackets you place to which type the object should be casted. Between the parentheses you place the object that is casted.
It is not possible to use static_cast on const objects to non-const objects. For this you have to use const_cast. (Further down we take a look at const_cast.)
If an automatic conversion is valid (from enum to int for instance) then you can use static_cast to do the opposite (from int to enum.)
For instance:

 enum my_numbers { a=10, c=100, e=1000 };

 const my_numbers b = static_cast<my_numbers> (50);
 const my_numbers d = static_cast<my_numbers> (500);

Note: We add some new values (b and d). These are type-cast from int to enum.

Reinterpret_cast

The reinterpret_cast is used for casts that are not safe:
  • Between integers and pointers
  • Between pointers and pointers
  • Between function-pointers and function-pointers
For instance the typecast from an integer to a character pointer:

 char *ptr_my = reinterpret_cast<char *>(0xb0000);

Note: the example above uses a fixed memory location.
If we use the reinterpret_cast on a null-pointer then we get a null-pointer of the asked type:

 char *ptr_my = 0;
 int *ptr_my_second = reinterpret_cast<int *>(ptr_my);

The reinterpret_cast is almost as dangerous as an “old fashion” cast. The only guaranty that you get is that if you cast an object back to the original data-type (before the first cast) then the original value is also restored (of course only if the data-type was big enough to hold the value.)
The only difference with an old fashion cast is that const is respected. This means that a reinterpret_cast can not be used to cast a const object to non-const object. For instance:

 char *const MY = 0;

 // This is not valid because MY is a const!!
 int *ptr_my = reinterpret_cast<int *>( MY);

Const_cast

The only way to cast away the const properties of an object is to use const_cast. Take a look at an example:

 void a(Person* b);

 int main()
 {
  const Person *ptr_my = new Person("Joe");
  a( const_cast<Person *>(ptr_my) );
 }

The use of const_cast on an object doesn’t guarantee that the object can be used (after the const is cast away.) Because it is possible that the const-objects are put in read-only memory by the program.
The const_cast can not be used to cast to other data-types, as it is possible with the other cast functions.
Take a look at the next example:

 int a;

 const char *ptr_my = "Hello";

 a  = const_cast<int *>(ptr_my);

 a = reinterpret_cast<const char*>(ptr_my);

 a = reinterpret_cast<int *>(const_cast<char *>(ptr_my) );

Note: casting from const char * to int * isn’t very good (not to say a very dirty trick.) Normally you won’t do this.
The first statement (const_cast) will give an error, because the const_cast can’t convert the type. The second statement (reinterpret_cast) will also give an error, because the reinterpret_cast can’t cast the const away. The third statement will work (mind the note. It is a dirty trick, better not use it.)
________________________________________________________

C++ Typecasting Part 2 – RTTI, dynamic_cast, typeid and type_info

Before you start this C++ programming tutorial on RTTI, dynamic_cast, typeid and type_info,
make sure you fully understand the previous tutorial on static_cast, const_cast and reinterpret_cast.

Runtime Type Information (RTTI)

Runtime Type Information (RTTI) is the concept of determining the type of any variable during execution (runtime.) The RTTI mechanism contains:
  • The operator dynamic_cast
  • The operator typeid
  • The struct type_info
RTTI can only be used with polymorphic types. This means that with each class you make, you must have at least one virtual function (either directly or through inheritance.)
Compatibility note: On some compilers you have to enable support of RTTI to keep track of dynamic types.
So to make use of dynamic_cast (see next section) you have to enable this feature. See you compiler documentation for more detail.

Dynamic_cast

The dynamic_cast can only be used with pointers and references to objects. It makes sure that the result of the type conversion is valid and complete object of the requested class. This is way a dynamic_cast will always be successful if we use it to cast a class to one of its base classes. Take a look at the example:

 class Base_Class { };
 class Derived_Class: public Base_Class { };

 Base_Class a; Base_Class * ptr_a;
 Derived_Class b; Derived_Class * ptr_b;

 ptr_a = dynamic_cast<Base_Class *>(&b);
 ptr_b = dynamic_cast<Derived_Class *>(&a);

The first dynamic_cast statement will work because we cast from derived to base. The second dynamic_cast statement will produce a compilation error because base to derived conversion is not allowed with dynamic_cast unless the base class is polymorphic.
If a class is polymorphic then dynamic_cast will perform a special check during execution. This check ensures that the expression is a valid and complete object of the requested class.
Take a look at the example:

// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;

class Base_Class { virtual void dummy() {} };
class Derived_Class: public Base_Class { int a; };

int main () {
  try {
  Base_Class * ptr_a = new Derived_Class;
  Base_Class * ptr_b = new Base_Class;
  Derived_Class * ptr_c;

     ptr_c = dynamic_cast< Derived_Class *>(ptr_a);
     if (ptr_c ==0) cout << "Null pointer on first type-cast" << endl;

     ptr_c = dynamic_cast< Derived_Class *>(ptr_b);
     if (ptr_c ==0) cout << "Null pointer on second type-cast" << endl;

   } catch (exception& my_ex) {cout << "Exception: " << my_ex.what();}
  return 0;
}

In the example we perform two dynamic_casts from pointer objects of type Base_Class* (namely ptr_a and ptr_b) to a pointer object of type Derived_Class*.
If everything goes well then the first one should be successful and the second one will fail. The pointers ptr_a and ptr_b are both of the type Base_Class. The pointer ptr_a points to an object of the type Derived_Class. The pointer ptr_b points to an object of the type Base_Class. So when the dynamic type cast is performed then ptr_a is pointing to a full object of class Derived_Class, but the pointer ptr_b points to an object of class Base_Class. This object is an incomplete object of class Derived_Class; thus this cast will fail!
Because this dynamic_cast fails a null pointer is returned to indicate a failure. When a reference type is converted with dynamic_cast and the conversion fails then there will be an exception thrown out instead of the null pointer. The exception will be of the type bad_cast.
With dynamic_cast it is also possible to cast null pointers even between the pointers of unrelated classes.
Dynamic_cast can cast pointers of any type to void pointer(void*).

Typeid and typ_info

If a class hierarchy is used then the programmer doesn’t have to worry (in most cases) about the data-type of a pointer or reference, because the polymorphic mechanism takes care of it. In some cases the programmer wants to know if an object of a derived class is used. Then the programmer can make use of dynamic_cast. (If the dynamic cast is successful, then the pointer will point to an object of a derived class or to a class that is derived from that derived class.) But there are circumstances that the programmer (not often) wants to know the prizes data-type. Then the programmer can use the typeid operator.
The typeid operator can be used with:
  • Variables
  • Expressions
  • Data-types
Take a look at the typeid example:

 #include <iostream>
 #include <typeinfo>
 using namespace std;

 int main ()
 {
  int * a;
  int b; 

  a=0; b=0;
  if (typeid(a) != typeid(b))
  {
   cout << "a and b are of different types:\n";
   cout << "a is: " << typeid(a).name() << '\n';
   cout << "b is: " << typeid(b).name() << '\n';
  }
  return 0;
  }

Note: the extra header file typeinfo.
The result of a typeid is a const type_info&. The class type_info is part of the standard C++ library and contains information about data-types. (This information can be different. It all depends on how it is implemented.)
A bad_typeid exception is thrown by typeid, if the type that is evaluated by typeid is a pointer that is preceded by a dereference operator and that pointer has a null value.
______________________________________________________

File IO in C++ (text and binary files)

This C++ programming language tutorial will be in two parts. The first (this one) will cover the theory behind IO and in the second tutorial we will look at some examples.

Input-Output

Input – Output is a process of transfer of data from one computer device to another or from one part of the computer to another.
There are three categories of Input-Output:
  • Standard IO
  • Memory IO
  • Network IO
Standard IO is used frequently for which C++ provides cin, cout, cerr and clog streams. IO in C++ is done through stream classes, which are having the following inheritance hierarchy:
Streams inheritance hierarchy
We can use the ifstream, ofstream and fstream classes to perform file IO. (cin is an object of class istream and cout is an object of class ostream.)
File IO means transfer of data from secondary memory (hard disk) to main memory or vice-versa. A schematic showing the flow of data and classes involved is as follows:
Data flow
Note: The arrows indicate the flow of data.

Text and binary files

The C++ language supports two types of files:
  • Text files
  • Binary files
The basic difference between text files and binary files is that in text files various character translations are performed such as “\r+\f” is converted into “\n”, whereas in binary files no such translations are performed.
By default, C++ opens the files in text mode.
In the tables below we will see the various steps and operations that can (or must) be performed to use files in C++:
1)Creating or opening a
file
  • For writing data
Text Files
ofstream out (“myfile.txt”);
or
ofstream out;
out.open(“myfile.txt”);
Binary Files
ofstream out (“myfile.txt”,ios::binary);
or
ofstream out;
out.open(“myfile.txt”, ios::binary);
  • For Appending (adding text at the end of the existing file)
Text Files
ofstream out(“myfile.txt”,ios::app);
or
ofstream out;
out.open(“myfile.txt”, ios::app);
Binary Files
ofstream out
(“myfile.txt”,ios::app|ios::binary);
or
ofstream out;
out.open(“myfile.txt”, ios::app | ios::binary);
  • For reading data
Text Files
ifstream in (“myfile.txt”);
or
ifstream in ;
in.open(“myfile.txt”);
Binary Files
ifstream in (“myfile.txt”, ios::binary);
or
ifstream in ;
in.open(“myfile.txt”, ios::binary);
2) Closing Files (after reading or writing)
ofstream object
“out”
Ifstream object
“in”
out.close();
in.close();
3) Reading / Writing Data to and from files
DataFunctions for reading
file
Function for writing
into file
charget();put();
1
word
>>
(extraction
operator)
<< (insertion
operator)
>=1
word
getline();<< (insertion
operator)
Objectsread()write()
Binary
data
Same as
above
Same as
above
4) Functions that can be used to perform special tasks
OperationfunctionDescription
Checking end of
file.
eof()Used to check eof during
the reading of file
Check if an operation
fails.
bad()
Returns true
if a reading or writing operation fails.
Check if an operation
fails.
Fail()
Returns true
in the same cases as bad(), but also in the case that a format error
happens.
Checking for opened
file.
is_open();Checks if the file is
opened or not, returns true if the file is opened else
false
Number of bytes already
read.
gcount()Returns count of the
bytes read from the file
Ignoring characters
during file read.
ignore()Ignores n bytes from the
file. (get pointer is positioned after n
character)
Checking next
character.
peek()Checks the next available
character, will not increase the get pointer to next
character.
Random access (only for
binary files).
seekg()
seekp()
tellg()
tellp()
In case of binary files random access is performed using these functions. They either give or set the position of get and put pointers on the particular location

__________________________________________________________

File IO in C++ (text and binary files) part II

In the first C++ programming tutorial on file IO we looked at the theory behind file IO.
In this second C++ programming tutorial on file IO we will look at some examples.

Read and Write characters (text file)

In the example below we will write and read characters from a text file.
We will write a character (given by the user) to a file test.txt as long as the answer to the question “Continue?:” is replayed with y (from yes). If we answer with something else the program must close the test.txt file. The last part of the program will read all characters in the file test.txt.

#include<iostream>
#include<fstream>
using namespace std;

int main(int argc, char *argv[])
{
 //code to write characters in a file
 char c, ans;
 ans='y';

 //Open an output stream
 ofstream out ("test.txt");
 if(out.is_open())
 {
  //Loop will continue until something other then y is entered
  while (ans=='y')
  {
   cout <<endl << "Continue ?";
   cin >> ans;
   if(ans=='y')
   {
    cout << endl << "Enter Character :";
    cin >> c;
    out.put(c);
   }
  }
 }
 out.close();

 //code for reading file completely
 ifstream in("test.txt");
 if(in.is_open())
 {
  while(!in.eof())
  {
   c = in.get();
   if(!in.eof())
   cout << c;
  }
 }
 in.close();
}

Special flags

The table below shows the special flags that can be used to manage files. (These flags are used during opening of the files).
ios::app
Opens the file in append mode
ios::ate
Opens the file and set the cursor at end of the
file
ios::binary
Opens the file in binary mode
ios::in
Opens the file for reading
ios::out
Opens the file for writing
ios::trunc
Opens the file and truncates all the contents from
it

Binary copy

In the next example we will copy a binary file to another binary file. We will open two files in binary mode,
one for reading and one for writing.
If both are open then we will copy the content of one file to another.

#include<iostream>
#include<fstream>
using namespace std;

int main(int argc, char *argv[])
{
 //Open an input and output stream in binary mode
 ifstream in("myimage.jpg",ios::binary);
 ofstream out("myimage1.jpg",ios::binary);

 if(in.is_open() && out.is_open())
 {
  while(!in.eof())
  {
   out.put(in.get());
  }
 }

 //Close both files
 in.close();
 out.close();
}

Note: of cource you need to supply the myimage.jpg file to copy (or any other jpg file).

Reading and writing an object

In this last example we will write and read an object (student object) to and from a file:

#include<iostream>
#include<fstream>
using namespace std;

class Student
{
 char name[20];
 int mark;
public:
 void GetStudentData();
 void ShowStudentData();
};

void Student ::  GetStudentData()
{
 cout << "Enter Student Name:" << endl;
 cin >> name;
 cout << "Enter Student Mark:" << endl;
 cin >> mark;
}

void Student ::  ShowStudentData()
{
 cout << "Student Details are:" << endl;
 cout << "Name: " <<  name << endl
  << "Mark: " <<  mark << endl;
}

int main(int argc, char *argv[])
{
 char ans='y';
 Student sobj;

 //We open student.dat in append mode
 ofstream out("student.dat", ios::app);

 if(out.is_open())
 {
  //Loop will continue until something other then y is entered
  while( ans == 'y')
  {
   cout << endl << "Continue ?";
   cin >> ans;
   if(ans == 'y')
   {
    sobj.GetStudentData();
    out.write((char*) & sobj, sizeof(sobj));
   }
  }
 }
 out.close();

 ifstream in("student.dat");
 if(in.is_open())
 {
  while(!in.eof())
  {
   in.read((char*) &sobj, sizeof(sobj));
   if(!in.eof())
   {
   sobj.ShowStudentData();
   }
  }
 }
 in.close();
}

As you can see there are many ways to do file IO in the C++ programming language.

No comments:

Post a Comment