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]);
}