User:Jimregan/C Primer chapter 5

Programming In C++: Elementary Features
The C++ language is an "object-oriented" extension to C that supports fundamental C syntax but adds many new elements. This chapter provides an overview of its differences from C, while the next chapter discusses its central concept, classes.

C++ Fundamentals
A C++ program may at first look a great deal like a C program, and in fact most ANSI C programs can be compiled just fine under C++, but there are some differences.

The first and most obvious is that comments have a simplified form. While in C you would state:

/* This is a comment. This is a second comment. This is the last comment here. */

-- in C++ you would say:

// This is a comment. // This is a second comment. // This is the last comment here.

The second thing you notice in C++ is that simple I/O is performed differently than in C. C++ defines three standard I/O files:

cin:     Console input. cout:    Console output. cerr:    Console error output device.

-- and input and output operators ("<<" and ">>") to go along with them. For example, the following program adds two numbers:

// getxy.cpp

#include 

void main {     double x, y;

cout << "Please enter X and Y.\n"; cin >> x;     cin >> y;      cout << "The sum of X and Y is " << x + y << ".\n"; }

This form of C++ I/O is not fussy about data types, and data formatting can be performed. For example, you can print data in different number formats:

// prbase.cpp

#include 

void main {     int n = 666; cout << dec << n << ' ' << oct << n << ' ' << hex << n << '\n'; }

This prints the value as follows:

666 1232 29a

There is much more that can be said about C++ "stream I/O", but it will have to wait for a later section.

While C++ supports the ANSI C function definition style:

int example( int x, int y );

-- it also has some enhancements. Default parameter values can be specified:

int example( int = 0, int = 100 );

This means that if you call "example" as follows:

example;

-- then the default parameter values 0 and "100" will be specified. If you call it as:

example( -100 );

-- the parameter values will be -100 and 100. If you specify both parameters:

example( 1, 1000 );

-- then the values are "1" and "1000". You cannot specify only the second parameter:

example( ,1000 );

There are some other enhancements in function handling that will be discussed in a later section.

C++ incorporates the following additional keywords:

class delete friend inline new operator private protected public this virtual

These new keywords will be discussed in detail later in this document.

C++ Extensions to C

 * One unusual difference of C++ relative to C is that it allows function prototyping within the "main" program. C++ is not picky about placement of declarations, although a function has to be prototyped before it is used.

Similarly, C++ is not fussy about the placement of variable declarations, and they can be declared within a block if need be. For example, the following is perfectly legal in C++:

// vardec.cpp

#include 

void main {     for( int l = 0; l < 5; l++ )  // Notice declaration of "l". {        cout << l << '\n'; }  }

There are some peculiarities in this declaration with respect to scope, however. The scope of the variable "l" is the current block, but only the portions of the current block after the declaration. Statements before the declaration cannot refer to that variable.

Another new feature of C++ is that you can refer to a global variable and a local variable that have the same name by using the "scope resolution operator", or "::". For example:

// prglob.cpp

#include 

int n = 42;                         // Global variable.

void main {    int n = 666;                       // Local variable. cout << ::n << ' ' << n << '\n';  // Print global, then local variable. }

This displays:

42 666

C++ has a new mechanism to replace the function macros of C, which you may recall can have unpredictable behavior. In C++ you can define "inline functions" with the syntax:

inline ifunc( int x ) {    ...   }

The code for this function is inserted in the program wherever the function is invoked, allowing for faster execution at the expense of memory usage.

C++ also features some improvements in the "const" keyword. In C the "#define" preprocessor metacommand is used to define constants:

#define PI 3.141592654

In C++, the "const" keyword is preferred:

const double PI 3.141592654;

If you don't specify the data type, C++ will assume "int". The advantage of the "const" keyword is that it allows constant declarations to be made inside functions, resulting in constant values that are "local" to that function. (Of course, they can be used to define global constants as well.)

Another (minor) difference is in C++ handling of enumerated types. If you declare an enumerated type:

enum Color { Red, White, Blue, Green, Cyan, Magenta, Yellow, Black };

-- and then you declare a variable of that type, you don't have to specify it as an enum type:

Color cvar;

You can still use standard C libraries in C++, but you must provide a "linkage specification" to tell the C++ compiler that it is using functions compiled for C. For example, if you want to use a function from the C standard library in a C++ program, you would have to write:

// prnrnd.cpp

#include 

extern "C" {    #include    }

void main {    cout << rand; }

C++ Memory Allocation -- Free & Delete
C++ has two new keywords, "new" and "delete", to perform variable memory allocation, in place of the C "malloc" and "free" routines. The "new" operator returns a pointer to memory allocated for a specified variable type from the "free store" reserved for the program, and the "delete" operator returns that memory to free store:

// alloc.cpp

#include 

struct Date {    int month; int day; int year; };

void main {    Date *event = new Date;   // Allocate memory for variable of type Date. event->month = 7; event->day = 4; event->year = 1863; cout << "What happened on " << event->day << '/' << event->month << '/' << event->year << "?\n"; delete event;            // Deallocate memory. }

You don't have to specify "struct Date", as you would in C. The "new" and "delete" operators provide a much cleaner interface than "malloc" and "free".

For another example, consider the dynamic allocation of arrays:

// dynaloc.cpp

#include   #include 

void main {     cout << "Array size: "; int size; cin >> size;                     // Get array size. int *array = new int[size];      // Allocate an array. for( int i = 0; i < size; i++ )  // Load it with random numbers. {       array[i] = rand; }     for( i = 0; i < size; i++ ) {       cout << array[i] << '\n'; }     delete array;                     // Return array to free store. }

Of course, if you dynamically allocate memory, there is a good chance that your program may run out of free store at some time, and so you need to be able to handle that circumstance. C++ includes a function named "set_new_handler" that allows you to specify a handler that will be invoked when the free store is exhausted:

// newhand.cpp

#include   #include    #include 

static void memhandler {    cerr << "Out of memory!\n"; exit(1); }

void main {    set_new_handler( memhandler ); long sum = 0; while (1) {      char *cp = new char[10000]; sum += 10000; cout << "Memory allocated: " << sum << '\n'; }  }

C++ Function Overloading
A particular innovation in C++ relative to C is the use of "function overloading". In standard C, if you defined a function named "cube" that used "int" values, then you would have to define a function with a different name that handled "double" values. C++ allows you to have multiple functions of the same name in the same program by using the "overload" keyword:

// cubes.cpp

#include 

long cube( long l ) {    return l * l * l;   }

double cube( double d ) {    return d * d * d;   }

void main {    long n1 = 3; double n2 = 5.0;

cout << cube( n1 ) << "\n" << cube( n2 ) << "\n"; }

This program defines a pair of overloaded functions. The type of parameter will determine which function will be called.

C++ Structures
C++ extends the concepts of data structures defined in C.

The first and simplest difference is that in C++ defining a structure creates a new data type. The only obvious consequence of this (as shown earlier) is that while in C every instance of a defined structure must be declared as a "struct":

struct Date { int month, day, year }; ...  struct Date today;       /* C structure instance declaration. */

-- in C++ the "struct" keyword is not required in the instance declaration. For an example of C++ structure declaration:

// cpstruct.cpp

#include 

struct Date {    int month; int day; int year; };

static void display( Date );    // Function proto with "Date" parameter.

void main {    Date bday = { 2, 16, 1953 }; cout << "My birthday is: " << '\n'; display( bday ); }

static void display( Date d ) {    static char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; cout << months[d.month-1] << ' ' << d.day << ", " << d.year; }

You can also declare arrays of structures:

Date days[20];

-- or pointers to them.

The first really significant difference in structure definition under C++ compared to C is that you can specify functions as elements of structures.

This is a big step toward the concept of object-oriented programming. Now, instead of having a data structure that simply stores data items, you can have functions to work with the data "encapsulated" in the structure.

The following example modifies the previous one to illustrate such encapsulation:

// encaps.cpp

#include 

struct Date {    int month, day, year; void display;               // Function bound to "Date" struct. };

void Date::display {    static char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; cout << months[month-1] << ' ' << day << ", " << year; }

void main {    Date bday = { 2, 16, 1953 }; cout << "My birthday is: " << '\n'; bday.display; }

There are some interesting things about this program. First, the encapsulated function is defined with:

void Date::display

-- to indicate that "display" is associated with the struct "Date". Note that a parameter of type "Date" is no longer defined, since the function is implicitly associated with a parameter of that form.

Similarly, the data fields are also directly accessible in the function:

cout << months[month-1] << ' ' << day << ", " << year;

It is not necessary to use the notation "d.months", "d.day", "d.year".

However, the function, being an element of that type of structure, is invoked using that notation:

bday.display;

Structures also allow function overloading. Different structures can have a function of the same name. For example:

// stover.cpp

#include   #include <stdio.h>   #include <time.h>

// *********************************************************************

struct Date // Date structure with a function named "display". {    int month, day, year; void display; };

void Date::display {    static char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; cout << months[month] << ' ' << day << ", " << year; }

// *********************************************************************

struct Time // Time structure with a function named "display". {    int hour, minute, second; void display; };

void Time::display {    char t[15]; sprintf( t, "%d:%2d:%2d %s",      ( hour > 12 ? hour - 12 : (hour == 0 ? 12 : hour )),      minute, second, ( hour < 12? "am" : "pm" )); cout << t;  }

// *********************************************************************  void main {    time_t ctime = time(0);             // Get current time. struct tm current = *localtime( &ctime );

Time now;                          // Instances of defined structures. Date today;

now.hour = current.tm_hour;        // Initialize Time structure. now.minute = current.tm_min; now.second = current.tm_sec;

today.month = current.tm_mon;      // Initialize Date structure. today.day = current.tm_mday; today.year = current.tm_year + 1900;

cout << "At the tone it will be "; // Display time and date. now.display; cout << " on "; today.display; cout << ".\n"; }

When invoked, this program produces an output of the form:

At the tone it will be 6:20:33 on March 17, 1995.

BACK_TO_TOP

[5.6] C++ REFERENCES


 * Along with the traditional means of passing parameters to functions as values:

int testfunc( int x );

-- or as pointers:

\Bint testfunc( int *x );

-- C++ provides an additional calling scheme, using "reference variables". This scheme is equivalent to using pointers, in that the function can change the value in the variable and return it to the calling program without having the overhead of passing all the data to the function. However, it doesn't require the use of pointer dereferencing operations in the target function, making it easier to pass structures and arrays to functions.

Reference variables are a slightly fuzzy concept, so please bear with the discussion. It will become clearer as elements are introduced.

A reference variable is defined as an "alias", or synonym, for a variable. It is defined using the "&" operator:

int some_int; int& ref_to_int = some_int;

This use of "&" is distinct from its use as the "address of" operator:

&some_int

-- which gives the address of a variable. The reference is initialized when it is declared -- naturally, since it doesn't have any independent existence.

The following example illustrates the behavior of reference variables:

// reftest.cpp

#include <iostream.h>

void main {    int some_int = 42; int& ref_to_int = some_int;

cout << some_int << ' ' << ref_to_int << '\n';  // Print values. ref_to_int++;                                   // Increment reference. cout << some_int << ' ' << ref_to_int << '\n';  // Print values. some_int++;                                     // Increment variable. cout << some_int << ' ' << ref_to_int << '\n';  // Print values. cout << &some_int << ' ' << &ref_to_int << '\n'; // Print addresses. }

This prints:

42 42  43 43   44 44   0x6555fffc 0x6555fffc

A reference, as this example shows, is not a copy of nor a pointer to the variable it aliases. It is simply another name for it.


 * References are generally used in conjunction with arrays, structures, and class objects, to be discussed in the next chapter. There's not really much reason to define references for simple "int" or "float" variables.

The reason for having references is to simplify parameter passing to functions, as noted earlier in this section. They eliminate the overhead of passing large data structures to functions, eliminate the need for pointer dereferencing operations, and allow a function to modify the data from the source program.

For example:

// ptrdemo.cpp

#include <iostream.h>

struct big    // A big structure that doesn't do much of anything. {    int sn; char text[1024]; } b = { 42, "Life, the Universe, and Everything!" };

void fslow( big b1 );  // Function to call the big structure by value. void ffast( big& b2 ); // Function to call the big structure by reference.

void main {    fslow( b );           // Slow, because data must be put on stack. ffast( b );          // Fast, because data is directly accessed. }

void fslow( big b1 ) {    cout << b1.sn << '\n' << b1.text << '\n'; }

void ffast( big& b2 ) {    cout << b2.sn << '\n' << b2.text << '\n'; }

A second example illustrates how references accomplish the same thing as pointers but don't require the clumsy pointer dereferencing syntax:

// refdemo.cpp

#include <iostream.h>

struct big    // Same big struct as before. {    int sn; char text[1024]; } b = { 42, "Life, the Universe, and Everything!" };

void fptr( big *b1 );   // Function to use pointers. void fref( big& b2 );   // Function to use references.

void main {    fptr( &b ); fref( b ); }

void fptr( big *b1 ) {     cout << b1->sn << '\n' << b1->text << '\n';  // Pointer deference. }

void fref( big& b2 ) {    cout << b2.sn << '\n' << b2.text << '\n';    // As before. }

A function can, as noted, change the value of the referenced variable in the calling program. If this is not desired, in the case where you simply want fast and economical parameter passing, you can declare a reference as a constant:

void func( const somestr& d ); // Parameter "d" can't be changed.

References can also be returned by functions to calling programs:

&getref( int i );         // Prototype for function to return reference. ...  somestr& r = getref( 3 );  // Return reference value to calling routine.

Remember that a reference is an alias. You cannot really perform any operation on a reference but initialize it, since any other operation you perform on the reference is simply performed on the variable referenced. You also cannot define arrays of references.

v2.0.7 / 6 of 7 / 01 feb 02 / greg goebel / public domain