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.
'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.
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.