A very brief introduction to C++

The C language was developed by Kernighan and Ritchie at Bell Labs in the early 1970s, as a systems-programming language for the Unix operating system. Some history is here.

Bjarne Stroustrup began work on adding objects to C in 1979; his book The C++ Programming Language was published. Originally, C++ compilers compiled first into C (eg with cfront), though this is increasingly difficult because C++ now contains some features, like exceptions, that do not have C analogues.

In C# and Java, everything is an object. In C++, objects are optional; indeed, for a long time C++ was touted as "a better C".

In C#, objects are created with new() on the so-called heap:

    Ratio r1 = new Ratio(3,5);

    List<string> slist = new List<string>();

The variables r1 and slist are "references" to the actual data; these are implemented via pointers.

In C++, objects can be local variables, allocated on the stack:
   
    Ratio r1(3,5)
    List<string> slist();

Digression into stack and heap memory

C# also has garbage collection: data that is on the heap but no longer referenced is reclaimed. This can be an inefficient process, though most modern garbage collectors are pretty fast. It almost always relies on being able to identify all pointers anywhere in the program, which can be difficult for C++.

Basic types

C++ has int, double and char. There are also long and unsigned variants of int and char.

Variables can be initialized three ways:
   
    int x = 0;
    int y(0);
    int z{0};     // new in 2012 standard; we won't use it

The second form is more reminiscent of a constructor. We could initialize Ratio objects as either
   
    Ratio r1(3,5);
    Ratio r2 = Ratio(3,5);

The former notation is meant to suggest calling the Ratio constructor (or ctor in C++-speak) with the parameters 3 and 5. The expression Ratio(3,5) represents an "anonymous" construction of a Ratio object, assigned to r2 in the second line above. Anonymous objects can appear anywhere.

Pointers

In C++ you can allocate memory on the heap, and you get back a pointer to it. But now it is an "explicit" pointer:

    Ratio* rp = new Ratio(3,5);

Now, rp is a pointer to Ratio and *rp is the Ratio itself:
   
    r1.add(*rp);
    (*rp).add(r1);
    rp->add(r1);        // special alternate notation

In C++, explicit pointers are often very common.

C++ also allows pointers to stack memory, via the & (address-of) operator:
  
    int x = 3;
    int y = 4;
    int * px = &x;
    int * p2 = &y;        // px points to x; p2 points to y
    (*p2)++;                // increments y
    p2 = px;                 // makes p2 now point to x!
    (*p2)++;                // increments x

C++ has a big problem with the lack of garbage collection: if an object is created on the stack, and later there are no further references to it, then at that point the object's memory is "lost". Careful strategies are needed to make sure that delete is called appropriately. We will look later at some special cases.

Arrays

In C++ we can create an array of 10 integers on the stack as follows:

    int A[10];

This is a little confusing; C# would say "int[] A".

What if we want the array to be dynamically allocated on the heap? Then we have to have a pointer to an array.

But (and here things get a little weird) a pointer to an int is the same as a pointer to an array of ints. We would thus have:

    int * A;
    A = new int[10];

We can now use A as if it were an array, writing A[3], etc.

Strictly speaking, an array is a const pointer. We can write

    int * p = A;    // p points to the same place as A

    for (int i = 0; i < 10; i++) {
        cout << *p << endl;        // print the thing p points to
        p++;
    }

This is strange. It dates back to C, and the idea that an array of ints is just a series of ints adjacent in memory:

  ──┬────┬────┬────┬────┬────┬─
    │ 37 │ 14 │ 59 │ 63 │ 28 │
  ──┴────┴────┴────┴────┴────┴──

If p is a pointer pointing to the cell holding 37, then incrementing p (p++) means making p point to the next cell. This is how pointer incrementing works in C and C++, even though in most cases incrementing p really means increasing p by the size of the object at hand, eg 4 for 4-byte integers.

An array in C/C++, in other words, is just one or more things laid out consecutively in memory.

A consequence is that *p is always synonymous with p[0].

If we have created an array with

    A = new int[n];

then we can free the memory A uses with

    delete[] A;

I/O

Most C++ output is done using the ostream mechanism. Here is a simple C++ helloworld program:

#include <iostream>

using namespace std;

int main() {
    cout << "hello" << ',' << ' ';
    cout << "world" << endl;
}

The object cout is the standard character-based output device, and the "<<" operator essentially means "print to". The endl object represents printing an end-of-line character. The print operations can be done individually or chained together, eg

    cout << "hello, " << "world" << endl;

Note that this is conceptually more efficient than

    cout << string("hello, ") + "world" << endl;

because the latter version involves creation of a temporary string object to hold the result of the concatenation. The former just prints each string in turn.

Classes

In C#, each function is defined to be public or private. In C++, there are public: and private: tags in the class definition; things are public or private based on the most recent tag. Actual functions and variables do not have "public"/"private" tags.

Also, classes in C++ must have a ";" after the final "}".

This might be a good time to review the Ratio example in both C# and C++:
   
    ratio.cs
    ratio.cpp

The most common practice in C++ is to separate function declarations from their definitions; we will come to that later.

In both C# and C++, class constructors look like functions with the same name as the class, but do not have a return type. In both C# and C++, the destructor name is the class name prefixed by "~". Destructors in C++ are more important, though, because they often have to do memory deallocation.

In C# and Java, everything is a class. A standalone program must contain a class which has a function

    static void Main(string[] args) {...}

In C++, control begins at the function

    int main() { ... }
   
or, if interaction with the command line is desired,

    int main(int argcount, char** argvector) { ... }

What is the type of argvector above?

Control begins at main() and objects are created if there are calls in the program to create them.

Strings

The standard string type is string. At the start of the file the following should appear:

    #include <string>

Then one can write
   
    string s1 = "hello";
    s1 += ", ";
    cout << s1 + "world" << endl;

Somewhat confusingly, one cannot write

    cout << "hello" + "world";        // WRONG

I get

    error: invalid operands of types "const char [6]" and "const char [6]" to binary "operator+"

It turns out that "hello" is of type "const char[6]", and not actually a string. It can be fixed this way:

    cout << string("hello") + "world";

Why does this work? Why don't we have to convert "world" as well?

Control Structures

There is no foreach loop, but the for loop is common:

    for (int i = 0; i< slist.Count; i++) {
        Console.WriteLine(slist[i]);
    }