C++ Features Not in C


Listed below are some features that are found in C++, not found in C, but still have nothing to do with Object Oriented Programming.

Casts

In C, if you want to cast an int to a long int, for example, you'd use
         int i=0;
         long l = (long) i;
In C++, you can use a function-like call to make the cast.
         long l = long(i);
It's easier to read. Since it's possible to create functions to perform casts involving user-defined types, this makes all the casts look consistent. For example, you may have a user-defined type -- complex numbers. You have a function that accepts an integer and casts it to a complex number:
         1 --> 1 + 0i (real part is 1 and imaginary part is 0)
Suppose your function call is named 'complex', then it may look like:
         Complex x;
         int i=1;
         x = complex(i);

Flexible Declarations

In C, all var declarations within a scope occur at the beginning of that scope. Thus, all global declartions must appear before any functions, and any local declarations must be made before any executable statements.

C++, on the other hand, allows you to mix data declarations with functions and executable statements. E.g. In C,

         void makeit(void)
         {
            float i;
            char *cp;

            /* imagine 2000 lines of code here */

            /* allocate 100 bytes for cp */
            cp = malloc(100);    /* 1st use of cp */

            for (i=0; i<100; ++i)      /* 1st use of i */
            {
               /* do something */
            }

            /* more code */
         }
In C++,
         void makeit(void)
         {
            // 2000 lines of code

            char *cp = new char[100];

            for (int i=1; i<10; i++)
            {
            }
         }

'struct' and 'union' Tags

In C, we would have this segment:
         struct foo {int a; float b;}
         struct foo f;
This declares a struct with the tag name 'foo' and then creates an instance of foo named f. Notice when you declare var of that struct, you have to say 'struct foo'. In C++, struct and union tags are considered to be type name, just as if they had been declared by the 'typedef' statement.
         struct foo {int a; float b;}
         foo f;
which is equivalent to the following in C:
         typedef struct
                 {
                     int a;
                     float b;
                 } foo;
         foo f;

'const'

In ANSI C, it also supports 'const', but C++'s 'const' is more flexible than C's. In both C and C++, a value declared as 'const' is inviolate; it may not be modified by any part of the program in any way. The most common use of 'const' values in C is to replace '#define' literal constants.
         #define MAX_CUSTOMERS   10
         const int MAX_CUSTOMERS = 10;
Thus,
         MAX_CUSTOMERS = 10;
         MAX_CUSTOMERS ++;
are both not acceptable. Note: since you cannot make changes to a 'const', each constant must be initialized when declared. The following is wrong:
         const int invalid;
In C++, you can do something like
         const int ArraySize = 100;
         int Array[ArraySize];
while in ANSI C, this would be flagged as an error.

More examples for 'const':

         const int v[] = {1, 2, 3, 4};

         const char* pc = "asdf";   // pointer to constant
         pc[3] = 'a';               // error
         pc = "ghjk";               // ok

         char *const cp = "asdf";   // constant pointer
         cp[3] = 'a';               // ok
         cp = "ghjk";               // error

         const char *const cpc = "asdf";  // const pointer to const
         cpc[3] = 'a';                    // error
         cpc = "ghjk";                    // error
Function call:
         char* strcpy (char* p, const char*q);  // cannot modify *q
Here, by declaring a pointer argument const, the function is prohibited from modifying the object pointed to, which makes perfect sense since strcpy copies q to p and you better preserve q.

The :: Operator

:: is called the scope resolution operator and is used to access an item hidden in the current scope. For example,
         #include 

         int a;

         main()
         {
            float a;

            a = 1.5;
            ::a = 2;

            cout << "local a=" << a << endl;
            cout << "global a=" << ::a << endl;
The :: operator says, "don't use the local a, use the one declared outside the scope".
Thus:
         local a=1.5
         global a=2
It is to be noted, however, that it's possible to use the global var without the :: operator:
         int x = 11;

         void f4()
         {
            int y = x;     // global x
            int x = 22;
            y = x;         // local x
         }

'new' and 'delete'

In C, all dynamic mem allocation is handled via library calls, such as 'malloc' and 'free'. Here's how a traditional C programs might allocate memory:
         void func(void)
         {
            int *i;

            i = (int *)malloc(sizeof(int));
            *i = 10;
            printf("%d", *i);
            free(i);
         }
In C++, there are new ways of dynamically allocating mem using operators called 'new' and 'delete', where 'new' replaces 'malloc' and 'delete' replaces 'free' in C. We could rewrite the above function as the following:
         void func()
         {
            int *i = new int;

            *i = 10;
            cout << *i;
            delete i;
         }
You'd probably agree this is a much clearer syntax and it's much easier to use as well. A couple more examples:
         int *i = new int[10];    //  an array of 10 integers
         int *i = new int(*)[10]; // an array of 10 pointers to integers
You can also intialize all the variables allocated by 'new':
         float *f = new float[50] (0.0);
Error Code: if 'new' fails to allocate any memory requested, it will return NULL;
Tip: Usually, right after a call to allocate memory, 'new' or 'malloc' check to see if any memory has been allocated. This can prevent your program from accessing a NULL pointer which is a disaster and which will cause a bus error.
Note: Don't mix the use of 'new' and 'delete' with that of 'malloc' and 'free'. i.e, always use either all the C lib calls 'malloc' and 'free' in your program to manage dynamic mem. OR use all 'new' and 'delete'. All the mem allocated by 'malloc' should be returned to the available mem pool by 'free' and the same holds for 'new' and 'delete'. I'd just use 'new' and 'delete'.

References

C can by clumsy sometimes. When you write a function to swap two integers, you have to pass the two integers into the function by reference:
         void swapint(int *a, int *b)
         {
            int temp;

            temp = *a;
            *a = *b;
            *b = temp;
         }
Here's the function call:
         swapint(&i1, &i2);
C++ supports a special type of identifier known as 'reference' &. It makes changing the parameter values in a function reletively painless. The above function can be rewritten in C++ as follows:
         void swapint(int &a, int &b)
         {
            int temp = a;
            a = b;
            b = temp;
         }
Function call:
         swapint(i1, i2);
When i1 and i2 are passed into the function, a POINTS to i1 and b POINTS to i2 instead of making local copies of i1 and i2. Now, whenever you refer to a or b in the function, you actually refer to i1 and i2. So, whatever changes you make to a or b, they will be reflected on i1 and i2.

Note

  1. This saves space as the case when you use * in C, but it's easier and clearer.
  2. For those who know Pascal, it's just like 'VAR' parameters.
Tip

If you want to pass a variable into a function by reference, and yet want to make sure that the variable doesn't get changed within the function, you can prefix the parameter with a const. For e.g.,

               void whatever(const int &data);

Function Overloading

In C, as in most other programming languages, every function must have a unique name. At times, it can be annoying. Imagine you want to have a function that returns the abs value of an integer.
         int abs(int i);
If you need to figure out the abs value of every possible available data type, you then have to write a function for each of the possible types:
         long labs(long l);
         double dabs(double d);
All those functions do the same thing -- return the abs value of the argument. Thus it seems silly to have a different name for each of those functions. C++ solves this by allowing you to create those functions with the same name. This is called overloading. For example, you can do the above in C++:
         int abs(int i);
         long abs(long l);
         double abs(double d);
And depending on the type of parameter you pass into the 'abs' func. C++ will select the right one. Note: What if the type of the parameter passed in is not identical to any of the available parameter types in the existing functions?
               abs('a');
               abs(3.1415F);
C++ will try to make the easiest conversion to match those parameter types in the funcion prototypes.
               abs('a');      // call int abs(int i)
               abs(3.1415F);  // call double abs(double d);
If no such conversion exists, then an error will occur.