Overview of C++ and Object-Oriented Design

Contents

  1. Object-Oriented Design vs Procedural Design
  2. The HelloWorld Procedure and the HelloWorld Object
  3. C++ Data Types
  4. Expressions
  5. Coding Style

1. Object-Oriented Design vs Procedural Design

Many of you will already be familiar with one or more procedural languages. Examples of such languages are FORTRAN 77, Pascal and C. In the procedural programming paradigm, one focuses on the decomposition of software into various functional components. In other words, the program is organized into a collection of functions, (also known as procedures or subroutines), which will be executed in a defined order to produce the desired result.

By contrast, object-based programming focuses on the organization of software into a collection of components, called objects, that group together

  1. Related items of data, known as properties.
  2. Operations that are to be performed on the data, which are known as methods.

In other words, an object is a model of a real world concept, which posesses both state and behavior.
Programming languages that allow us to create objects are said to support abstract data types. Examples of such languages are CLU, Ada, Modula-2.

The object-oriented programming paradigm goes a step beyond abstract data types by adding two new features: inheritance and polymorphism. We will talk about these ideas in depth later, but for now it will be sufficient to say that their purpose is to facilitate the management of objects that have similar characteristics. For example, squares, triangles and circles are all instances of shapes. Their common properties are that they all have a centroid and an area. Their common method might be that they all need to be displayed on the screen of a computer. Examples of languages that support the object-oriented paradigm are C++, Java® and Smalltalk.

2. The HelloWorld Procedure and the HelloWorld Object

Let's take a look two simple programs that print out the string, Hello World!

The HelloWorld Procedure

Here is the procedural version of the program, written in C. The first statement is a preprocessor directive that tells the compiler to include the contents of the header file stdio.h. We include this file because it declares the existence of the built-in function, printf(). Every C program must have a top-level function named main(), which provides the entry point to the program. 

#include <stdio.h>

/* The HelloWorld procedure definition. */
void HelloWorld() {
    printf("Hello World!\n");
}
 

/* The main program. */
int main() {
    HelloWorld();               /* Execute the HelloWorld procedure. */
    return 0;                      /* Indicates successful completion of the program. */
}

The HelloWorld Object

Here is the object-based version of the program, written in C++. We have created a new data type, HelloWorld, that is capable of printing out the words we want. In C++, the keyword class is used to declare a new data type. Our class has three publicly accessible methods, HelloWorld(), ~HelloWorld() and print(). The first two methods have special significance and they are respectively known as a constructor and the destructor. The constructor has the same name as the class. It is an initialization method that will be automatically invoked whenever we create a HelloWorld object. The destructor also has the same name as the class, but with a ~ prefix. It is a finalization method that will be automatically invoked whenever a HelloWorld object is destroyed. In our class, the print() method is the only one that actually does anything useful.

It is important to understand the distinction between a class and an object. A class is merely a template for creating one or more objects. Our main program creates a single object named a based on the class definition that we have provided. We then send the object a "print" message by selecting and invoking the print() method using the . operator. We are able to access the print() method in main() because we have made it a public member function of the class.
 

#include <stdio.h>

// The HelloWorld class definition.
class HelloWorld {
        public:
    HelloWorld() {}            // Constructor.
    ~HelloWorld() {}          // Destructor.
    void print() {
        printf("Hello World!\n");
    }
};                                      // Note that a semicolon is required here.
 

// The main progam.
int main() {
    HelloWorld a;           // Create a HelloWorld object.
    a.print();                  // Send a "print" message to the object.
    return 0;
}

C++ as a Superset of C

Although C++ is generally thought of as an object-oriented language, it does support the procedural progamming paradigm as well. In fact, C++ supports all the features of C in addition to providing new features of its own. For example, a C++ program may include C-style comments that use the /* */ delimiters as well C++-style comments that use the // syntax.

/* C-style comments are also allowed in C++. */

// Alternative comment syntax that is only allowed in C++.

3. C++ Data Types

Built-in Data Types

(Ref. Lippman 3.1, 3.2)

C++ built-in data types are similar to those found in C. The basic built-in types include

  • A boolean type: bool (only available in Standard C++).
  • Character types: char, unsigned char and wchar_t (wchar_t supports wide characters and is only available in Standard C++).
  • Integer types: short (or short int), int (or long int), unsigned short and unsigned int.
  • Floating point types: float, double and long double.

Not all computing platforms agree on the actual size of the built-in data types, but the following table indicates the typical sizes on a 32-bit platform:
 

BUILT-IN DATA TYPE SIZE IN BYTES
char, unsigned char 1
short, unsigned short 2
wchar_t, bool,
int, unsigned int, float 
4
double 8
long double 8 or 16

A literal constant is a constant value of some type. Examples of literal constants are
 

DATA TYPE LITERAL CONSTANT
char 'a', '7'
wchar_t L'a', L'7'
bool true, false
long int 8L, 8l
unsigned long int 8UL, 8ul
float 2.718F, 2.718f
double 2.718, 1e-3
long double 2.718L, 2.718l

Note that there is no built-in data type for strings. Strings can be represented as character arrays or by using the string type provided in the Standard C++ library. A string literal constant is of the form

"Hello World!"

User-defined Data Types

We have already seen how we can define new data types by writing a class, and for the most part we will use classes. However, C++ also provides extended support for C-style structures. For example, C++ allows member functions to be packaged in a struct in addition to member data. The most significant difference between a class and a struct is that by default, the members of a class are private, whereas the members of a struct are public.

struct date {
    int day;
    int month;
    int year;
    void set_date(int d, int m, int y);    // Member functions only allowed in C++.
};

int main() {
    struct date a;   /* C-style definition. */
    date a;            // Allowable C++ definition.
}
 

Pointer Types

(Ref. Lippman 3.3)

Pointer variables (or pointers) are a powerful concept that allow us to manipulate objects by their memory address rather than by their name. It is important to understand pointers clearly since they are used extensively in this course and in real world software.

A pointer must convey two pieces of information, both of which are necessary to access the object that it points to:

  1. the memory address of the object
  2. the type of the object

The following example illustrates the use of a pointer to an object of type double. The pointer is defined by the statement

double *p;

p can now hold the memory address of a double object, such as d. We obtain the address of d by applying the address of operator, &d, and we then store it in p. Now that p contains a valid address, we can refer to the object d by applying the dereference operator, *p. Notice that we have used * in two different contexts, with different meanings in each case. The meaning of & also depends on the context in which it is used.

#include <stdio.h>

int main() {
    double d;         // An double object.
    double *p;       // A variable that is a pointer to an double.

    p = &d;          // Take the memory address of d and store it in p.

    d = 7.0;          // Store a double precision number in d.
    printf("The value of the object d is %lf\n", d);
    printf("The value of the object that p points to is %lf\n", *p);
    printf("The address of the object that p points to is %u\n", p);
}
 
Here is the output from a trial run:

The value of the object d is 7.000000
The value of the object that p points to is 7.000000
The address of the object that p points to is 4026528296

Reference Types

(Ref. Lippman 3.6)

As an added convenience, C++ provides reference types, which are an alternative way to use the functionality that pointers provide. A reference is just a nickname for existing storage.

The following example defines an integer object, i, and then it defines a reference variable, r, by the statement

int& r = i;

Be careful not to confuse this use of & with the address of operator. Also note that, unlike a pointer, a reference must be initialized at the time it is defined.

#include <stdio.h>

int main() {
    int i = 0;
    int& r = i;    // Create a reference to i.

    i++;
    printf("r = %d\n", r);
}

Explicit Type Conversion

(Ref. Lippman 4.14)

Explicit type conversion can be performed using a cast operator. The following code shows three alternative ways to explicitly convert an int to a float.

int main() {
    int a;
    float b;

    a = 3;
    b = (float)a;                           /* C-style cast operator. */
    b = float(a);                          // Alternative type conversion notation allowed in C++.
    b = static_cast<float>(a);    // A second alternative, allowed only in Standard C++.
}

const Keyword

(Ref. Lippman 3.5)

The const keyword is used to designate storage whose contents cannot be changed. A const object must be initialized at the time it is defined.

const int i = 10;    /* Allowed both in C and C++. */
const int j;            /* This is illegal. */

Variable Definitions

In C++, variable definitions may occur practically anywhere within a code block. A code block refers to any chunk of code that lies within a pair of scope delimiters, {}. For example, the following C program requires i and j to be defined at the top of the main() function.

#include <stdio.h>

int main() {
    int i, j;    /* C requires variable definitions to be at the top of a code block. */

    for (i = 0; i < 5; i++) {
         printf("Done with C\n");
    }
    j = 10;
}

In the C++ version of the program, we can define the variables i and j when they are first used.

#include <stdio.h>

int main() {
    for (int i = 0; i < 5; i++) {         // In Standard C++, i is available anywhere within the for loop.
        printf("Still learning C++\n");
     }
    int j = 10;
}

4. Expressions

(Ref. Lippman 4.1-4.5, 4.7, 4.8, 4.13, 4.14)

Operator Precedence

An expression consists of one or more operands and a set of operations to be applied to them. The order in which operators are applied to operands is determined by operator precedence. For example, the expression

1 + 4 * 3 / 2 == 7 && !0

is evaluated as

((1 + ((4 * 3) / 2)) == 7) && (!0)

Note that the right hand side of the logical AND operator is only evaluated if the left hand side evaluates to true. (For a table of operator precedence, see Lippman, Table 4.4.)

Arithmetic Conversions

The evaluation of arithmetic expressions follows two general guidelines:

  1. Wherever necessary, types are promoted to a wider type in order to prevent the loss of precision.
  2. Integral types (these are the various boolean, character and integer types) are promoted to the int data type prior to evaluation of the expression.

5. Coding Style

Coding styles tend to vary from one individual to another. While you are free to develop your own style, it is important to make your code consistent and readable. Software organizations frequently try to enforce consistency by developing a set of coding guidelines for programmers.

Here is an example of an inconsistent coding style. The curly braces in the two for loops are aligned differently. The second style is usually preferred because it is more compact and it avoids excessive indentation.
 

#include <stdio.h>

int main() {
    int i;

   for (i = 0; i < 5; i++)
       {
            printf("This convention aligns the curly braces.\n");
       }

    for (i =0; i < 5; i++) {
        printf("This is a more compact convention which aligns ");
        printf("the closing brace with the for statement.\n");
    }
}