Sunday, 8 July 2012

Classes in C++


C++ Class Overview

1. Introduction:-

This note introduces some simple C++ concepts and outlines a subset of C++ that is easier to learn and use than the full language. Although we originally wrote this note for explaining the C++ used in the Nachos project, I believe it is useful to anyone learning C++. I assume that you are already somewhat familiar with C concepts like procedures, for loops, and pointers; these are pretty easy to pick up from reading Kernighan and Ritchie's ``The C Programming Language.''
I should admit up front that I am quite opinionated about C++, if that isn't obvious already. I know several C++ purists (an oxymoron perhaps?) who violently disagree with some of the prescriptions contained here; most of the objections are of the form, ``How could you have possibly left out feature X?'' However, I've found from teaching C++ to nearly 1000 undergrads over the past several years that the subset of C++ described here is pretty easy to learn, taking only a day or so for most students to get started.
The basic premise of this note is that while object-oriented programming is a useful way to simplify programs, C++ is a wildly over-complicated language, with a host of features that only very, very rarely find a legitimate use. It's not too far off the mark to say that C++ includes every programming language feature ever imagined, and more. The natural tendency when faced with a new language feature is to try to use it, but in C++ this approach leads to disaster.
Thus, we need to carefully distinguish between (i) those concepts that are fundamental (e.g., classes, member functions, constructors) -- ones that everyone should know and use, (ii) those that are sometimes but rarely useful (e.g., single inheritance, templates) -- ones that beginner programmers should be able to recognize (in case they run across them) but avoid using in their own programs, at least for a while, and (iii) those that are just a bad idea and should be avoided like the plague (e.g., multiple inheritance, exceptions, overloading, references, etc).
Of course, all the items in this last category have their proponents, and I will admit that, like the hated goto, it is possible to construct cases when the program would be simpler using a goto or multiple inheritance. However, it is my belief that most programmers will never encounter such cases, and even if you do, you will be much more likely to misuse the feature than properly apply it. For example, I seriously doubt an undergraduate would need any of the features listed under (iii) for any course project (at least at Berkeley this is true). And if you find yourself wanting to use a feature like multiple inheritance, then, my advice is to fully implement your program both with and without the feature, and choose whichever is simpler. Sure, this takes more effort, but pretty soon you'll know from experience when a feature is useful and when it isn't, and you'll be able to skip the dual implementation.
A really good way to learn a language is to read clear programs in that language. I have tried to make the Nachos code as readable as possible; it is written in the subset of C++ described in this note. It is a good idea to look over the first assignment as you read this introduction. Of course, your TA's will answer any questions you may have.
You should not need a book on C++ to do the Nachos assignments, but if you are curious, there is a large selection of C++ books at Cody's and other technical bookstores. (My wife quips that C++ was invented to make researchers at Bell Labs rich from writing ``How to Program in C++'' books.) Most new software development these days is being done in C++, so it is a pretty good bet you'll run across it in the future. I use Stroustrup's "The C++ Programming Language" as a reference manual, although other books may be more readable. I would also recommend Scott Meyer's ``Effective C++'' for people just beginning to learn the language, and Coplien's ``Advanced C++'' once you've been programming in C++ for a couple years and are familiar with the language basics. Also, C++ is continually evolving, so be careful to buy books that describe the latest version.

C in C++
To a large extent, C++ is a superset of C, and most carefully written ANSI C will compile as C++. There are a few major caveats though:
1.                  All functions must be declared before they are used, rather than defaulting to type int.
2.                  All function declarations and definition headers must use new-style declarations, e.g.,
3.                         extern int foo(int a, char* b);
The form extern int foo(); means that foo takes no arguments, rather than arguments of an unspecified type and number. In fact, some advise using a C++ compiler even on normal C code, because it will catch errors like misused functions that a normal C compiler will let slide.
4.                  If you need to link C object files together with C++, when you declare the C functions for the C++ files, they must be done like this:
5.                         extern "C" int foo(int a, char* b);
Otherwise the C++ compiler will alter the name in a strange manner.
6.                  There are a number of new keywords, which you may not use as identifiers --- some common ones are new, delete, const, and class.

 

Basic Concepts:-

Before giving examples of C++ features, I will first go over some of the basic concepts of object-oriented languages. If this discussion at first seems a bit obscure, it will become clearer when we get to some examples.
1.                  Classes and objects. A class is similar to a C structure, except that the definition of the data structure, and all of the functions that operate on the data structure are grouped together in one place. An object is an instance of a class (an instance of the data structure); objects share the same functions with other objects of the same class, but each object (each instance) has its own copy of the data structure. A class thus defines two aspects of the objects: the data they contain, and the behavior they have.
2.                  Member functions. These are functions which are considered part of the object and are declared in the class definition. They are often referred to as methods of the class. In addition to member functions, a class's behavior is also defined by:
1.                                          What to do when you create a new object (the constructor for that object) -- in other words, initialize the object's data.
2.                                          What to do when you delete an object (the destructor for that object).
3.                  Private vs. public members. A public member of a class is one that can be read or written by anybody, in the case of a data member, or called by anybody, in the case of a member function. A private member can only be read, written, or called by a member function of that class.

2. Classes:-


Classes are used for two main reasons: (1) it makes it much easier to organize your programs if you can group together data with the functions that manipulate that data, and (2) the use of private members makes it possible to do information hiding, so that you can be more confident about the way information flows in your programs.
C++ classes are similar to C structures in many ways. In fact, a C++ struct is really a class that has only public data members. In the following explanation of how classes work, we will use a stack class as an example.
1.                  Member functions. Here is a (partial) example of a class with a member function and some data members:
2.                         class Stack {
3.                           public:
4.                             void Push(int value); // Push an integer, checking for overflow.
5.                             int top;          // Index of the top of the stack.
6.                             int stack[10];    // The elements of the stack.
7.                         };
8.                          
9.                         void
10.                     Stack::Push(int value) {
11.                         ASSERT(top < 10);                         // stack should never overflow
12.                         stack[top++] = value;
13.                     }
This class has two data members, top and stack, and one member function, Push. The notation class:: function denotes the function member of the class class. (In the style we use, most function names are capitalized.) The function is defined beneath it.
As an aside, note that we use a call to ASSERT to check that the stack hasn't overflowed; ASSERT drops into the debugger if the condition is false. It is an extremely good idea for you to use ASSERT statements liberally throughout your code to document assumptions made by your implementation. Better to catch errors automatically via ASSERTs than to let them go by and have your program overwrite random locations.
In actual usage, the definition of class Stack would typically go in the file stack.h and the definitions of the member functions, like Stack::Push, would go in the file stack.cc.
If we have a pointer to a Stack object called s, we can access the top element as s->top, just as in C. However, in C++ we can also call the member function using the following syntax:
    s->Push(17);
Of course, as in C, s must point to a valid Stack object.
Inside a member function, one may refer to the members of the class by their names alone. In other words, the class definition creates a scope that includes the member (function and data) definitions.
Note that if you are inside a member function, you can get a pointer to the object you were called on by using the variable this. If you want to call another member function on the same object, you do not need to use the this pointer, however. Let's extend the Stack example to illustrate this by adding a Full() function.
class Stack {
  public:
    void Push(int value); // Push an integer, checking for overflow.
    bool Full();       // Returns TRUE if the stack is full, FALSE otherwise.
    int top;          // Index of the lowest unused position.
    int stack[10];    // A pointer to an array that holds the contents.
};
bool
Stack::Full() {
    return (top == 10);
}
Now we can rewrite Push this way:
void
Stack::Push(int value) {
    ASSERT(!Full());
    stack[top++] = value;
}
We could have also written the ASSERT:
    ASSERT(!(this->Full());
but in a member function, the this-> is implicit.
The purpose of member functions is to encapsulate the functionality of a type of object along with the data that the object contains. A member function does not take up space in an object of the class.
14.              Private members. One can declare some members of a class to be private, which are hidden to all but the member functions of that class, and some to be public, which are visible and accessible to everybody. Both data and function members can be either public or private.
In our stack example, note that once we have the Full() function, we really don't need to look at the top or stack members outside of the class -- in fact, we'd rather that users of the Stack abstraction not know about its internal implementation, in case we change it. Thus we can rewrite the class as follows:
class Stack {
  public:
    void Push(int value); // Push an integer, checking for overflow.
    bool Full();       // Returns TRUE if the stack is full, FALSE otherwise.
  private:
    int top;          // Index of the top of the stack.
    int stack[10];    // The elements of the stack.
};
Before, given a pointer to a Stack object, say s, any part of the program could access s->top, in potentially bad ways. Now, since the top member is private, only a member function, such as Full(), can access it. If any other part of the program attempts to use s->top the compiler will report an error.
You can have alternating public: and private: sections in a class. Before you specify either of these, class members are private, thus the above example could have been written:
class Stack {
    int top;          // Index of the top of the stack.
    int stack[10];    // The elements of the stack.
  public:
    void Push(int value); // Push an integer, checking for overflow.
    bool Full();       // Returns TRUE if the stack is full, FALSE otherwise.
};
Which form you prefer is a matter of style, but it's usually best to be explicit, so that it is obvious what is intended. In Nachos, we make everything explicit.
What is not a matter of style: all data members of a class should be private. All operations on data should be via that class' member functions. Keeping data private adds to the modularity of the system, since you can redefine how the data members are stored without changing how you access them.
15.              Constructors and the operator new. In C, in order to create a new object of type Stack, one might write:
16.                         struct Stack *s = (struct Stack *) malloc(sizeof (struct Stack));
17.                         InitStack(s, 17);
The InitStack() function might take the second argument as the size of the stack to create, and use malloc() again to get an array of 17 integers.
The way this is done in C++ is as follows:
    Stack *s = new Stack(17);
The new function takes the place of malloc(). To specify how the object should be initialized, one declares a constructor function as a member of the class, with the name of the function being the same as the class name:
class Stack {
  public:
    Stack(int sz);    // Constructor:  initialize variables, allocate space.
    void Push(int value); // Push an integer, checking for overflow.
    bool Full();       // Returns TRUE if the stack is full, FALSE otherwise.
  private:
    int size;         // The maximum capacity of the stack.
    int top;          // Index of the lowest unused position.
    int* stack;       // A pointer to an array that holds the contents.
};
 
Stack::Stack(int sz) {
    size = sz;
    top = 0;
    stack = new int[size];   // Let's get an array of integers.
}
There are a few things going on here, so we will describe them one at a time.
The new operator automatically creates (i.e. allocates) the object and then calls the constructor function for the new object. This same sequence happens even if, for instance, you declare an object as an automatic variable inside a function or block -- the compiler allocates space for the object on the stack, and calls the constructor function on it.
In this example, we create two stacks of different sizes, one by declaring it as an automatic variable, and one by using new.
void
test() {
    Stack s1(17);
    Stack* s2 = new Stack(23);
}
Note there are two ways of providing arguments to constructors: with new, you put the argument list after the class name, and with automatic or global variables, you put them after the variable name.
It is crucial that you always define a constructor for every class you define, and that the constructor initialize every data member of the class. If you don't define your own constructor, the compiler will automatically define one for you, and believe me, it won't do what you want (``the unhelpful compiler''). The data members will be initialized to random, unrepeatable values, and while your program may work anyway, it might not the next time you recompile (or vice versa!).
As with normal C variables, variables declared inside a function are deallocated automatically when the function returns; for example, the s1 object is deallocated when test returns. Data allocated with new (such as s2) is stored on the heap, however, and remains after the function returns; heap data must be explicitly disposed of using delete, described below.
The new operator can also be used to allocate arrays, illustrated above in allocating an array of ints, of dimension size:
    stack = new int[size];
Note that you can use new and delete (described below) with built-in types like int and char as well as with class objects like Stack.
18.              Destructors and the operator delete. Just as new is the replacement for malloc(), the replacement for free() is delete. To get rid of the Stack object we allocated above with new, one can do:
19.                         delete s2;
This will deallocate the object, but first it will call the destructor for the Stack class, if there is one. This destructor is a member function of Stack called ~Stack():
class Stack {
  public:
    Stack(int sz);    // Constructor:  initialize variables, allocate space.
    ~Stack();         // Destructor:   deallocate space allocated above.
    void Push(int value); // Push an integer, checking for overflow.
    bool Full();      // Returns TRUE if the stack is full, FALSE otherwise.
  private:
    int size;         // The maximum capacity of the stack.
    int top;          // Index of the lowest unused position.
    int* stack;       // A pointer to an array that holds the contents.
};
 
Stack::~Stack() {
    delete [] stack;  // delete an array of integers
}
The destructor has the job of deallocating the data the constructor allocated. Many classes won't need destructors, and some will
Example:-

class Point {
    int _x, _y;       // point coordinates
 
  public:             // begin interface section
    void setX(const int val);
    void setY(const int val);
    int getX() { return _x; }
    int getY() { return _y; }
  };
 
  Point apoint;
 
 
  void Point::setX(const int val) {
    _x = val;
  }
 
  void Point::setY(const int val) {
    _y = val;
  }
 
  apoint.setX(1);     // Initialization
  apoint.setY(1);


  void Point::setX(const int val) {
    this->_x = val;   // Use this to reference invoking
                      // object
  }
 
  void Point::setY(const int val) {
    this->_y = val;
  }
Constructors 
class Point {
    int _x, _y;
 
  public:
    Point() {
      _x = _y = 0;
    }
 
    void setX(const int val);
    void setY(const int val);
    int getX() { return _x; }
    int getY() { return _y; }
  };
  class Point { 
    int _x, _y; 
     
  public: 
    Point() {
      _x = _y = 0; 
    }  
    Point(const int x, const int y) {
      _x = x;
      _y = y;
    }
     
    void setX(const int val);
    void setY(const int val); 
    int getX() { return _x; } 
    int getY() { return _y; } 
  };
Point apoint;           // Point::Point()
  Point bpoint(12, 34);   // Point::Point(const int, const int)


class Point {  
    int _x, _y;  
      
  public:  
    Point() { 
      _x = _y = 0;  
    }   
    Point(const int x, const int y) {
      _x = x;
      _y = y; 
    } 
    Point(const Point &from) {
      _x = from._x;
      _y = from._y;
    }
      
    void setX(const int val); 
    void setY(const int val);  
    int getX() { return _x; } 
    int getY() { return _y; }  
  };
  Point bpoint(apoint);    // Point::Point(const Point &)
  Point cpoint = apoint;   // Point::Point(const Point &)
    ~Point() { /* Nothing to do! */ }
 

Other Basic C++ Features:-

Here are a few other C++ features that are useful to know.
1.                  When you define a class Stack, the name Stack becomes usable as a type name as if created with typedef. The same is true for enums.
2.                  You can define functions inside of a class definition, whereupon they become inline functions, which are expanded in the body of the function where they are used. The rule of thumb to follow is to only consider inlining one-line functions, and even then do so rarely.
As an example, we could make the Full routine an inline.
class Stack {
   ...
   bool Full() { return (top == size); };
   ...
};
There are two motivations for inlines: convenience and performance. If overused, inlines can make your code more confusing, because the implementation for an object is no longer in one place, but spread between the .h and .c files. Inlines can sometimes speed up your code (by avoiding the overhead of a procedure call), but that shouldn't be your principal concern as a student (rather, at least to begin with, you should be most concerned with writing code that is simple and bug free). Not to mention that inlining sometimes slows down a program, since the object code for the function is duplicated wherever the function is called, potentially hurting cache performance.
3.                  Inside a function body, you can declare some variables, execute some statements, and then declare more variables. This can make code a lot more readable. In fact, you can even write things like:
4.                         for (int i = 0; i < 10; i++) ;
Depending on your compiler, however, the variable i may still visible after the end of the for loop, however, which is not what one might expect or desire.
5.                  Comments can begin with the characters // and extend to the end of the line. These are usually more handy than the /* */ style of comments.
6.                  C++ provides some new opportunities to use the const keyword from ANSI C. The basic idea of const is to provide extra information to the compiler about how a variable or function is used, to allow it to flag an error if it is being used improperly. You should always look for ways to get the compiler to catch bugs for you. After all, which takes less time? Fixing a compiler-flagged error, or chasing down the same bug using gdb?
For example, you can declare that a member function only reads the member data, and never modifies the object:
class Stack {
   ...
   bool Full() const;  // Full() never modifies member data
   ...
};
As in C, you can use const to declare that a variable is never modified:
    const int InitialHashTableSize = 8;
This is much better than using #define for constants, since the above is type-checked.
7.                  Input/output in C++ can be done with the >> and << operators and the objects cin and cout. For example, to write to stdout:
8.                             cout << "Hello world!  This is section " << 3 << "!";
This is equivalent to the normal C code
    fprintf(stdout, "Hello world!  This is section %d!\n", 3);
except that the C++ version is type-safe; with printf, the compiler won't complain if you try to print a floating point number as an integer. In fact, you can use traditional printf in a C++ program, but you will get bizarre behavior if you try to use both printf and << on the same stream. Reading from stdin works the same way as writing to stdout, except using the shift right operator instead of shift left. In order to read two integers from stdin:
    int field1, field2;
    cin >> field1 >> field2;
        // equivalent to fscanf(stdin, "%d %d", &field1, &field2);
        // note that field1 and field2 are implicitly modified
In fact, cin and cout are implemented as normal C++ objects, using operator overloading and reference parameters, but (fortunately!) you don't need to understand either of those to be able to do I/O in C++.

 

Function members in classes:-

   Functions declared inside a class can be any of the following four types. This C++ Tutorial explains each one of them as below.

Ordinary member functions :

   These are ordinary functions defined with a return type and parameters. The return type can also be void. The special trait about member functions is they 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. The sample below this C++ Tutorial uses an ordinary member function Add(), returning an integer value.

Constructors:

   Constructors in C++ are special member functions of a class. They have the same name as the Class Name. There can be any number of overloaded constructors inside a class, provided they have a different set of parameters. There are some important qualities for a constructor to be noted.
·                     Constructors have the same name as the class.
·                     Constructors do not return any values
·                     Constructors are invoked first when a class is initialized. Any initializations for the class members, memory allocations are done at the constructor.
   In the example class given below in this C++ tutorial has the constructor Example_Class(), with the same name as the class.

Destructors:

   Destructors in C++ also have the same name, except for the fact that they are preceded by a '~' operator. The destructors are called when the object of a class goes out of scope. It is not necessary to declare a constructor or a destructor inside a class. If not declared, the compiler will automatically create a default one for each. If the constructor/destructor is declared as private, then the class cannot be instantiated. Check below for the sample class of the C++ tutorial for an example of destructor.

3. Access Control:-

   The classes in C++ have 3 important access levels. They are Private, Public and Protected. The explanations are as follows.

Private:

   The members are accessible only by the member functions or friend functions.

Protected:

   These members are accessible by the member functions of the class and the classes which are derived from this class.

Public:

   Accessible by any external member. Look at the sample class below.

Example of a class:

   class Example_class //Sample Class for the C++
   {
       private:
         int x; //Data member
         int y; // Data member
       public:
         Example_Class() //Constructor for the C++
         {
             x = 0;
             y = 0;
         }
       ~Example_Class() //destructor for the C++
       { }
      int Add()
      {
         return x+y;
      }
};

4. Constructors:-

*                    Are used to initialize the member variables of the class when the objects of the class are created
*                    Must have the same name as that of class name
*                    Cannot return any value, not even a void type
*                    Class can have more than one constructors defined in it (known as overloaded constructors)
*                    Default constructors accept no parameters and are automatically invoked by the compiler


Need for Constructors

*                    To initialize a member variable at the time of declaration


Declaration of Constructors

Example:

            class Calculator
               {      
            private:
                          int number1, number2, tot;
            public:
            ...
            Calculator()
            {
            number1 = number2 = tot = 0;
            cout << "Constructor invoked"<< endl;
            }
            };





Destructors:-

*                    Are used to de-initialize the objects when they are destroyed
*                    Are used to clear memory space occupied by a data member when an object goes out of scope
*                    Must have the same name as that of the class, preceded by a ~ (For example: ~Calculator())
*                    Are automatically invoked
*                    Can also be explicitly invoked when required
*                    Cannot be overloaded

Need for Destructors

*                    To de-initialize the objects when they are destroyed
*                    To clear memory space occupied by a data member when an object goes out of scope

Declaration of Destructors

Example:

            /* This Code Shows The Use Of Destructor In The Calculator Class */
            class Calculator
            {
            private:
                                    int number1, number2, tot;
            public:
                                    ...
                        ~Calculator()   //Body Of The                                                            //Destructor
                        {
                        number1 = number2 = tot = 0;
                        }
            };

Just a Minute…
Given is a code snippet for the main() function of a program. How would you ensure that the following tasks are accomplished when the program is executed:
1. The member variables are initialized with zero
2. On exit or termination of the program the following message is displayed : “Bye Folks!!! Have a Nice Time”
#include <iostream>
int main()
{
            Number num1;num1.disp();
}
                        Hint: The Number class  has only one member variable                                 myNum.
Scope Resolution Operator (::)
*                    Is used to define member functions outside the class definition therefore making the class definition more readable
*                    Example:
class calculator
{
public:
            void input();
};
   void calculator::input ()
  {…}



Constructors with Parameters
*                    Allow member variables of the class to be initialized with user supplied values from main() function also called parameters
Example
class calculate
{
private:
int num1,num2,total;
public:
calculate(int, int);
};
calculate::calculate(int x, int y)
{
num1=x;num2=y;
total=0;
}
int main()
{//Accept values in two variable var1 & //var2
calculate c1(var1,var2);
}
Constructors with Default Arguments
calculate::calculate(int x, int y=10);

int main()
{
calculate c1(5);
}
Invoking Member Functions
*                    By using call by value
*                    By using call by reference
Call by Value
*                    Is useful when the function does not need to modify the values of the original variables
*                    Does not affect the values of the variables in caller functions
Problem Statement 5.P.1
Predict the Output:
#include <iostream>
void square(int);
class functionCall
{
            int number;
            public:
            functionCall();
};
functionCall::functionCall()
{
            number=10;square(number);
}
void square(int num)
{
            cout<<num<<endl;
            num *= num; //This Expression Is //Resolved As num = num * num
            cout << num << endl;
}
int main()
{
            functionCall f1;
            return 0;
                        }         
Call by Reference
*                    Is implemented by using
·                                                         An alias
·                                                         Pointers

Call by Reference (Contd.)
*                    Using an alias
·                     The same variable can be referenced by more than one name by using the & or the alias operator
·                     The change in the value of the variable by the called or the calling program is reflected in all the affected functions
Call by Reference using Pointers
To define and declare a pointer variable:
*                    Using pointers
·                     Involves a pointer variable storing the memory address of any variable
·                     Is advantageous since it allows direct access to individual bytes in the memory and output devices

5. Parameter Passing Methods :-
·                     Overview
·                     Value Parameters
·                     Reference Parameters
·                     Const Reference Parameters
·                     Array Parameters

In Java, all parameters are passed by value. In C++, a parameter can be passed by:
1.                  value,
2.                  reference, or
3.                  const-reference
Each parameter's mode is determined by the way it is specified in the function's header (the mode is the same for all calls to the function). For example:
void f( int a, int &b, const int &c );
Parameter a is a value parameter, b is a reference parameter, and c is a const-reference parameter.

Value Parameters

When a parameter is passed by value, a copy of the parameter is made. Therefore, changes made to the formal parameter by the called function have no effect on the corresponding actual parameter. For example:
void f(int n) {
    n++;
}
 
int main() {
    int x = 2;
    f(x);
    cout << x;  
}
In this example, f's parameter is passed by value. Therefore, although f increments its formal parameter n, that has no effect on the actual parameter x. The value output by the program is 2 (not 3).
Note that if a pointer is passed by value, then although the pointer itself is not affected by changes made to the corresponding formal parameter, the object pointed by the pointed can be changed. For example:
void f(int *p) {
    *p = 5;
    p = NULL;
}
 
int main() {
    int x=2;
    int *q = &x;
    
    f(q);
 
    // here, x == 5, but q != NULL
}
In this example, f's parameter is passed by value. Therefore, the assignment p = NULL; in f has no effect on variable q in main (since f was passed a copy of q, not q itself). However, the assignment *p = 5: in f, does change the value pointed to by q. To understand why, consider what happens when the example program runs:
After executing the two statements:
    int x=2;
    int *q = &x;
 
memory looks like this:
 
       +---+
    x: | 2 | <--+
       +---+    |
                |
       +---+    |
    q: | --|----+
       +---+
 
Now function f is called; the value of q (which is the address of x)
is copied into a new location named p:
 
 
       +---+
    x: | 2 | <--+  <--+
       +---+    |     |
                |     |
       +---+    |     |
    q: | --|----+     |
       +---+          |
                      |
       +---+          |
    p: | --|----------+
       +---+
 
Executing the two statements in f:
    *p = 5;
    p = NULL;
 
causes the values of x (the thing pointed to by p) and p to be changed:
 
       +---+
    x: | 5 | <--+
       +---+    |
                |
       +---+    |
    q: | --|----+
       +---+     
                 
       +----+     
    p: |NULL|
       +----+
 
However, note that q is NOT affected.

Reference Parameters

When a parameter is passed by reference, conceptually, the actual parameter itself is passed (and just given a new name -- the name of the corresponding formal parameter). Therefore, any changes made to the formal parameter do affect the actual parameter. For example:
void f(int &n) {
    n++;
}
 
int main() {
    int x = 2;
    f(x);
    cout << x;  
}
In this example, f's parameter is passed by reference. Therefore, the assignment to n in f is actually changing variable x, so the output of this program is 3.
When you write a function whose purpose is to compute two or more values, it makes sense to use reference parameters (since a function can return only one result). For example, if you want to read a list of integers from a file, and you want to know both how many integers were read, as well as the average value that was read, you might use a function like the following:
void f(istream &input, int &numRead, double &average) {
    int k, sum = 0;
    numRead = 0;
 
    while (intput >> k) {
        numRead++;
        sum += k;
    }
    average = (double)sum/numRead;
}
Another common use of reference parameters is for a function that swaps two values:
void swap( int &j, int &k ) {
    int tmp = j;
    j = k;
    j = tmp;
}
This is useful, for example, in sorting an array, when it is often necessary to swap two array elements. The following code swaps the jth and kth elements of array A:
swap(A[j], A[k]);

Const-Reference Parameters

Another reason to use reference parameters is when you don't want the function to modify an actual parameter, but the actual parameter is very large, and you want to avoid the overhead of creating a copy. Of course, this only works if the function does not modify its formal parameter. To be sure that the actual parameter is not "accidentally" modified, you should use a const-reference parameter. Declaring the parameter to be const tells the compiler that it should not be changed; if the function does change the parameter, you will get a compile-time warning (possibly an error on some systems). For example:
void f(const IntList &L) {
-- the code here cannot modify L or the compiler will complain --
}
The potential use of a const-reference parameter is the reason why member functions that do not modify any data members should be declared const. For example, suppose that the IntList Print member function was not declared const. Then the following code would cause a compile-time error:
void f(const IntList &L) {
  L.Print(cout);
}
Because L is a const-reference parameter, it is the compiler's job to be sure that L is not modified by f (and that means that no data members of L are modified). The compiler doesn't know how the Print function is implemented; it only knows how it was declared, so if it is not declared const, it assumes the worst, and complains that function f modifies its const-reference parameter L.

Array Parameters

Another unfortunate thing about C++ arrays is that they are always passed by reference (even though you don't declare them to be reference parameters). For example:
void f(int A[]) {
    A[0] = 5;
}
 
int main() {
    int B[10];
    B[0] = 2;
    f(B);
    cout << B[0] << endl;  // the output is 5
}
Although f's parameter looks like it is passed by value (there is no &), since it is an array it is actually passed by reference, so the assignment to A[0] is really assigning to B[0], and the program prints 5 (not 2).
If you want to pass an array by value, you should use a vector, not a regular C++ array (see the last section in the notes on C++ classes for information about vectors).





7. Inline Functions:-

When a function is declared inline, the function is expanded at the calling block. The function is not treated as a separate unit like other normal functions.
   But a compiler is free to decide, if a function qualifies to be an inline function. If the inline function is found to have larger chunk of code, it will not be treated as an inline function, but as like other normal functions.
   Inline functions are treated like macro definitions by the C++ compiler. They are declared with the keyword inline as follows.

   //Declaration for C++ Tutorial inline sample:
    int add(int x,int y);

   //Definition for C++ Tutorial inline sample:
    inline int add(int x,int y)
    {
        return x+y;
    }
    In fact, the keyword inline is not necessary. If the function is defined with its body directly and the function has a smaller block of code, it will be automatically treated as inline by the compiler. 
   As implied, inline functions are meant to be used if there is a need to repetitively execute a small block of code, which is smaller. When such functions are treated inline, it might result in a significant performance difference.

8.Static Data Members:-
Classes can contain static member data and member functions. When a data member is declared as static, only one copy of the data is maintained for all objects of the class. (For more information, see Static Member Functions.)
Static data members are not part of objects of a given class type; they are separate objects. As a result, the declaration of a static data member is not considered a definition. The data member is declared in class scope, but definition is performed at file scope. These static members have external linkage. The following example illustrates this:
// static_data_members.cpp
class BufferedOutput
{
public:
   // Return number of bytes written by any object of this class.
   short BytesWritten()
   {
      return bytecount;
   }

   // Reset the counter.
   static void ResetCount()
   {
      bytecount = 0;
   }

   // Static member declaration.
   static long bytecount;
};

// Define bytecount in file scope.
long BufferedOutput::bytecount;

int main()
{
}
In the preceding code, the member bytecount is declared in class BufferedOutput, but it must be defined outside the class declaration.
Static data members can be referred to without referring to an object of class type. The number of bytes written using BufferedOutput objects can be obtained as follows:
long nBytes = BufferedOutput::bytecount;
For the static member to exist, it is not necessary that any objects of the class type exist. Static members can also be accessed using the member-selection (. and –>) operators. For example:
BufferedOutput Console;

long nBytes = Console.bytecount;
In the preceding case, the reference to the object (Console) is not evaluated; the value returned is that of the static object bytecount.
Static data members are subject to class-member access rules, so private access to static data members is allowed only for class-member functions and friends. These rules are described in Member-Access Control. The exception is that static data members must be defined in file scope regardless of their access restrictions. If the data member is to be explicitly initialized, an initializer must be provided with the definition.
The type of a static member is not qualified by its class name. Therefore, the type of BufferedOutput::bytecount is long.

 

 

 

 

 

9. The this pointer:-

The keyword this identifies a special type of pointer. Suppose that you create an object named x of class A, and class A has a nonstatic member function f(). If you call the function x.f(), the keyword this in the body of f() stores the address of x. You cannot declare the this pointer or make assignments to it.
A static member function does not have a this pointer.
The type of the this pointer for a member function of a class type X, is X* const. If the member function is declared with the const qualifier, the type of the this pointer for that member function for class X, is const X* const.
A const this pointer can by used only with const member functions. Data members of the class will be constant within that function. The function is still able to change the value, but requires a const_cast to do so:
void foo::p() const{    
member = 1;                       // illegal    
const_cast <int&> (member) = 1;   // a bad practice but legal 
}
A better technique would be to declare member mutable.
If the member function is declared with the volatile qualifier, the type of the this pointer for that member function for class X is volatile X* const. For example, the compiler will not allow the following:
struct A {
  int a;
  int f() const { return a++; }
};
The compiler will not allow the statement a++ in the body of function f(). In the function f(), the this pointer is of type A* const. The function f() is trying to modify part of the object to which this points.
The this pointer is passed as a hidden argument to all nonstatic member function calls and is available as a local variable within the body of all nonstatic functions.
For example, you can refer to the particular class object that a member function is called for by using the this pointer in the body of the member function. The following code example produces the output a = 5:
#include <iostream>
using namespace std;
 
struct X {
private:
  int a;
public:
  void Set_a(int a) {
 
    // The 'this' pointer is used to retrieve 'xobj.a'
    // hidden by the automatic variable 'a'
    this->a = a;
  }
   void Print_a() { cout << "a = " << a << endl; }
};
 
int main() {
  X xobj;
  int a = 5;
  xobj.Set_a(a);
  xobj.Print_a();
}
In the member function Set_a(), the statement this->a = a uses the this pointer to retrieve xobj.a hidden by the automatic variable a.
Unless a class member name is hidden, using the class member name is equivalent to using the class member name with the this pointer and the class member access operator (->).
The example in the first column of the following table shows code that uses class members without the this pointer. The code in the second column uses the variable THIS to simulate the first column's hidden use of the this pointer:

10. Dynamic Memory Allocation and Deallocation:-

Until now, in all our programs, we have only had as much memory available as we declared for our variables, having the size of all of them to be determined in the source code, before the execution of the program. But, what if we need a variable amount of memory that can only be determined during runtime? For example, in the case that we need some user input to determine the necessary amount of memory space.
The answer is dynamic memory, for which C++ integrates the operators new and delete.

Operators new and new[]

In order to request dynamic memory we use the operator new. new is followed by a data type specifier and -if a sequence of more than one element is required- the number of these within brackets []. It returns a pointer to the beginning of the new block of memory allocated. Its form is:
pointer = new type
pointer = new type [number_of_elements]
The first expression is used to allocate memory to contain one single element of type type. The second one is used to assign a block (an array) of elements of type type, where number_of_elements is an integer value representing the amount of these. For example:
Int *bobby;
Bobby = new int[5];
this case, the system dynamically assigns space for five elements of type int and returns a pointer to the first element of the sequence, which is assigned to bobby. Therefore, now, bobby points to a valid block of memory with space for five elements of type int.
The first element pointed by bobby can be accessed either with the expression bobby[0] or the expression *bobby. Both are equivalent as has been explained in the section about pointers. The second element can be accessed either with bobby[1] or *(bobby+1) and so on...
You could be wondering the difference between declaring a normal array and assigning dynamic memory to a pointer, as we have just done. The most important difference is that the size of an array has to be a constant value, which limits its size to what we decide at the moment of designing the program, before its execution, whereas the dynamic memory allocation allows us to assign memory during the execution of the program (runtime) using any variable or constant value as its size.
The dynamic memory requested by our program is allocated by the system from the memory heap. However, computer memory is a limited resource, and it can be exhausted. Therefore, it is important to have some mechanism to check if our request to allocate memory was successful or not.
C++ provides two standard methods to check if the allocation was successful:
One is by handling exceptions. Using this method an exception of type bad_alloc is thrown when the allocation fails. Exceptions are a powerful C++ feature explained later in these tutorials. But for now you should know that if this exception is thrown and it is not handled by a specific handler, the program execution is terminated.
This exception method is the default method used by new, and is the one used in a declaration like:
bobby = new int[5]; // if it fails an exception is thrown
The other method is known as nothrow, and what happens when it is used is that when a memory allocation fails, instead of throwing a bad_alloc exception or terminating the program, the pointer returned by new is a null pointer, and the program continues its execution.
This method can be specified by using a special object called nothrow, declared in header <new>, as argument for new:
bobby = new (nothrow) int [5]; 

In this case, if the allocation of this block of memory failed, the failure could be detected by checking if bobby took a null pointer value:
int * bobby;
bobby = new (nothrow) int [5];
if (bobby == 0) {
  // error assigning memory. Take measures.
  };
This nothrow method requires more work than the exception method, since the value returned has to be checked after each and every memory allocation, but I will use it in our examples due to its simplicity. Anyway this method can become tedious for larger projects, where the exception method is generally preferred. The exception method will be explained in detail later in this tutorial.

Operator delete and delete[]

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

delete pointer;
delete [] pointer;
The first expression should be used to delete memory allocated for a single element, and the second one for memory allocated for arrays of elements.
The value passed as argument to delete must be either a pointer to a memory block previously allocated with new, or a null pointer (in the case of a null pointer, delete produces no effect).

// rememb-o-matic
#include <iostream>
#include <new>
using namespace std;
 
int main ()
{
  int i,n;
  int * p;
  cout << "How many numbers would you like to type? ";
  cin >> i;
  p= new (nothrow) int[i];
  if (p == 0)
    cout << "Error: memory could not be allocated";
  else
  {
    for (n=0; n<i; n++)
    {
      cout << "Enter number: ";
      cin >> p[n];
    }
    cout << "You have entered: ";
    for (n=0; n<i; n++)
      cout << p[n] << ", ";
    delete[] p;
  }
  return 0;
}
Output:- 
 
How many numbers would you like to type? 5
Enter number : 75
Enter number : 436
Enter number : 1067
Enter number : 8
Enter number : 32
You have entered: 75, 436, 1067, 8, 32,

Notice how the value within brackets in the new statement is a variable value entered by the user (i), not a constant value:
p= new (nothrow) int[i];
But the user could have entered a value for i so big that our system could not handle it. For example, when I tried to give a value of 1 billion to the "How many numbers" question, my system could not allocate that much memory for the program and I got the text message we prepared for this case (Error: memory could not be allocated). Remember that in the case that we tried to allocate the memory without specifying the nothrow parameter in the new expression, an exception would be thrown, which if it's not handled terminates the program.
It is a good practice to always check if a dynamic memory block was successfully allocated. Therefore, if you use the nothrow method, you should always check the value of the pointer returned. Otherwise, use the exception method, even if you do not handle the exception. This way, the program will terminate at that point without causing the unexpected results of continuing executing a code that assumes a block of memory to have been allocated when in fact it has not.




11. Exception Handling:-
Exceptions are run-time anomalies, such as division by zero, that require immediate handling when encountered by your program. The C++ language provides built-in support for raising and handling exceptions. With C++ exception handling, your program can communicate unexpected events to a higher execution context that is better able to recover from such abnormal events. These exceptions are handled by code that is outside the normal flow of control.
The try, catch, and throw Statements
The following syntax shows a try block and its handlers:
try {
   // code that could throw an exception
}
[ catch (exception-declaration) {
   // code that executes when exception-declaration is thrown
   // in the try block
}
[catch (exception-declaration) {
   // code that handles another exception type
} ] . . . ]
// The following syntax shows a throw expression:
throw [expression]
 Remarks
The C++ language provides built-in support for handling anomalous situations, known as exceptions, which may occur during the execution of your program. The try, throw, and catch statements implement exception handling. With C++ exception handling, your program can communicate unexpected events to a higher execution context that is better able to recover from such abnormal events. These exceptions are handled by code that is outside the normal flow of control. The Microsoft C++ compiler implements the C++ exception handling model based on the ANSI C++ standard.
C++ also provides a way to explicitly specify whether a function can throw exceptions. You can use exception specifications in function declarations to indicate that a function can throw an exception. For example, an exception specification throw(...) tells the compiler that a function can throw an exception, but doesn't specify the type, as in this example:
void MyFunc() throw(...) {
   throw 1;
}
For more information, see Exception Specifications.
The code after the try clause is the guarded section of code. The throw expression throws (raises) an exception. The code block after the catch clause is the exception handler, and catches (handles) the exception thrown by the throw expression. The exception-declaration statement indicates the type of exception the clause handles. The type can be any valid data type, including a C++ class. If the exception-declaration statement is an ellipsis (...), the catch clause handles any type of exception, including C exceptions and system- or application-generated exceptions such as memory protection, divide by zero, and floating-point violations. Such a handler must be the last handler for its try block.
The operand of throw is syntactically similar to the operand of a return statement.
Execution proceeds as follows:
1.                  Control reaches the try statement by normal sequential execution. The guarded section within the try block is executed.
2.                  If no exception is thrown during execution of the guarded section, the catch clauses that follow the try block are not executed. Execution continues at the statement after the last catch clause following the try block in which the exception was thrown.
3.                  If an exception is thrown during execution of the guarded section or in any routine the guarded section calls (either directly or indirectly), an exception object is created from the object created by the throw operand. (This implies that a copy constructor may be involved.) At this point, the compiler looks for a catch clause in a higher execution context that can handle an exception of the type thrown (or a catch handler that can handle any type of exception). The catch handlers are examined in order of their appearance following the try block. If no appropriate handler is found, the next dynamically enclosing try block is examined. This process continues until the outermost enclosing try block is examined.
4.                  If a matching handler is still not found, or if an exception occurs while unwinding, but before the handler gets control, the predefined run-time function terminate is called. If an exception occurs after throwing the exception, but before the unwind begins, terminate is called.
5.                  If a matching catch handler is found, and it catches by value, its formal parameter is initialized by copying the exception object. If it catches by reference, the parameter is initialized to refer to the exception object. After the formal parameter is initialized, the process of unwinding the stack begins. This involves the destruction of all automatic objects that were constructed (but not yet destructed) between the beginning of the try block associated with the catch handler and the exception's throw site. Destruction occurs in reverse order of construction. The catch handler is executed and the program resumes execution following the last handler (that is, the first statement or construct which is not a catch handler). Control can only enter a catch handler through a thrown exception, never via a goto statement or a case label in a switch statement.
The following is a simple example of a try block and its associated catch handler. This example detects failure of a memory allocation operation using the new operator. If new is successful, the catch handler is never executed:
// exceptions_trycatchandthrowstatements.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main() {
   char *buf;
   try {
      buf = new char[512];
      if( buf == 0 )
         throw "Memory allocation failure!";
   }
   catch( char * str ) {
      cout << "Exception raised: " << str << '\n';
   }
}
The operand of the throw expression specifies that an exception of type char * is being thrown. It is handled by a catch handler that expresses the ability to catch an exception of type char *. In the event of a memory allocation failure, this is the output from the preceding example:
Exception raised: Memory allocation failure!
The real power of C++ exception handling lies not only in its ability to deal with exceptions of varying types, but also in its ability to automatically call destructor functions during stack unwinding, for all local objects constructed before the exception was thrown.
The following example demonstrates C++ exception handling using classes with destructor semantics:
 Example
// exceptions_trycatchandthrowstatements2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
void MyFunc( void );
class CTest {
public:
   CTest() {};
   ~CTest() {};
   const char *ShowReason() const { 
      return "Exception in CTest class."; 
   }
};
 
class CDtorDemo {
public:
   CDtorDemo();
   ~CDtorDemo();
};
 
CDtorDemo::CDtorDemo() {
   cout << "Constructing CDtorDemo.\n";
}
 
CDtorDemo::~CDtorDemo() {
   cout << "Destructing CDtorDemo.\n";
}
 
void MyFunc() {
   CDtorDemo D;
   cout<< "In MyFunc(). Throwing CTest exception.\n";
   throw CTest();
}
 
int main() {
   cout << "In main.\n";
   try {
       cout << "In try block, calling MyFunc().\n";
       MyFunc();
   }
   catch( CTest E ) {
       cout << "In catch handler.\n";
       cout << "Caught CTest exception type: ";
       cout << E.ShowReason() << "\n";
   }
   catch( char *str )    {
       cout << "Caught some other exception: " << str << "\n";
   }
   cout << "Back in main. Execution resumes here.\n";
}
 
In main.
In try block, calling MyFunc().
Constructing CDtorDemo.
In MyFunc(). Throwing CTest exception.
Destructing CDtorDemo.
In catch handler.
Caught CTest exception type: Exception in CTest class.
Back in main. Execution resumes here.
 Comments
Note that in this example, the exception parameter (the argument to the catch clause) is declared in both catch handlers:
catch( CTest E )
// ...
catch( char *str )
// ...
You do not need to declare this parameter; in many cases it may be sufficient to notify the handler that a particular type of exception has occurred. However, if you do not declare an exception object in the exception-declaration, you will not have access to that object in the catch handler clause.
A throw-expression with no operand re-throws the exception currently being handled. Such an expression should appear only in a catch handler or in a function called from within a catch handler. The re-thrown exception object is the original exception object (not a copy). For example:
try {
   throw CSomeOtherException();
}
catch(...) {  // Handle all exceptions
   // Respond (perhaps only partially) to exception
   throw;       // Pass exception to some other handler
}
 
 

No comments:

Post a Comment

Write your openion about my blog spot..To get automatic facebook updates like my Pagehttps://www.facebook.com/shivashankar4u ..It takes only 1 min to write the comment and to like the page.. Thanks.