Department of Engineering

IT Services

C++ course - Introduction

Introduction

The 1A/1B courses introduced all C++'s loop constructions and many of the operators. Array and class creation was also introduced. However some issues (e.g. Makefiles) were glossed over, others were used in artificial conditions, and many things were not touched upon at all. Some of these newer things are easy conceptually, and fundamental to modern C++ programming, though implementation details can be complicated. The pay-off comes when you use the Standard Library - a library which exploits advanced C++ features to make life easy for the programmer. This course hopes to bridge the gap between knowing the basics of C++ and being able to write project-sized applications that use the Standard Library.

These talks will cover a few key subjects. Most of your learning will be done between the talks. Whichever of the suggested methods of learning you decide to adopt, feel free to send me questions and exercise solutions.

C++ can be used in various ways: procedural (like Fortran 77 or C); Object-orientated; Generic, etc. It's up to you which ways you want to work, but whatever your choice sooner or later you'll need to know about the C++ concepts that help support these ways of working: Namespaces, Templates and Exceptions. This first talk attempts to introduce these concepts without worrying too much about the details of syntax.

Revision Quiz

But first, some revision. Look at the following code (the line numbers aren't part of the code)

int main() {        [0]
int i;              [1]
class test {        [2]
public:             [3]
  int data;         [4]
  int i;            [5]
  test() {data=1;}; [6]
};                  [7]
                    [8]
test t;             [9]
                   [10]
i=3;               [11]
data=2;            [12]
}                  [13]
  1. Which line stops this program compiling?
  2. Does the test class have a user-defined constructor? No Yes
  3. Is t a class or an object?
  4. In line 11, i is set to 3, but which i? The one created on line 1 or the one created on line 5?

C++ and other languages

Before you choose C++ you may wish to consider

  • Java - less trendy than it used to be?
  • Objective-C - iPodTouch Apps!
  • Fortran* - Faster?
  • C# - Microsoft!

The most damning comparison I've recently seen is from Fortran 90 for Physics Students

we regard C++ as the weakest of the object oriented languages. Objective C is a far more solid and well designed OOPS language, C++ is really some OOPS capability slapped on top of C. C++ is consequently extremely inefficient, inconsistent, overly large, and enormously difficult to program in. The experience of our clients mirrors our own, and in fact many DoE and DoD laboratories are finding that their headlong rush to C++ has been a hideously expensive mistake. I know of several C++ scientific coding projects there that consumed millions of dollars and tens of man-years, only to be abandoned because the resulting code was enormously inefficient on both traditional serial computers and on their large parallel supercomputers. Similar horror stories abound throughout the programming community at this point.

Bill Gates claimed that his biggest mistake in designing their new NT operating system was adopting C++ for the graphics coding, the resulting code took years longer to write than it should have and ran terribly slow.

Etc. I think this was written around 1995. Since then a lot has changed - ANSI C++ didn't officially appear until 1998 and at the time of writing, "C++11" is supported by few compilers in full. However, Ken Thompson (one of the Unix creators, and then a Google "Distinguished Engineer") more recently said "Google is C++, strictly C++. It's no big deal programming in C++, but I don't like it ... by and large I think it's a bad language". For more comments, see some quotes from Programmers at Work.

Despite all that, JAVA and the C family remain the 2 most popular languages according to the October 2010 figures by TIOBE Ranking Index. If you're worried about speed, don't look at comparison articles older than 5 years or so - compilers and programmer's understanding of C++ has improved a lot. At the most recent Stroustrup talk I went to, he showed how C++ could be faster than Fortran, even for simple 3-matrix operations.

C and C++

C++ grew out of C, but tried to remain as compatible as possible with it. This means that C++ has all the strengths of C++ but also all its weaknesses. See if you can find the C bugs.

C has meanwhile pursued its own course of development. C99 has features (e.g. variable-length arrays) which didn't appear in C++ until "C++11".

Several C++ features were added to improve upon risky or cumbersome C features, which is why some people think that learning C isn't a good way to start learning C++. C++ people nowadays use strings instead of character arrays, and increasingly use vectors instead of arrays. I suggest you do the same, unless you're good at finding C bugs. These pages will try to cover C++ as if C didn't exist.

Object-orientated programming

Some often mentioned advantages of object-oriented programming are: faster development, increased quality, easier maintenance, understandability and enhanced modifiability. Implementation details are hidden and interfaces provide precise and detailed instructions for the use of objects. Furthermore, an object-oriented approach is in many cases more natural and appeals more to human cognition than other methodologies.

Some important object-oriented (O-O) concepts are

  • Objects and messages - in a procedural language data is passed around from routine to routine. In an O-O language, messages are sent to objects and the object is trusted to do what it's told, operating on the data held within it.
  • Encapsulation - it's considered a good idea to keep together data and the routines which operate on that data, and to tightly control which routines are allowed to access the data (information hiding).
  • Code reuse - if you want to create a object that's like an existing object, you can use the existing object as a base for the new object. This is known as Inheritance.

The aim is to create objects that have tightly related parts but few connections with anything else. As an analogy consider a lightbulb: as long as it has a standard interface other components don't have to worry about how it's designed internally, and the lightbulb can easily be replaced by one that's internally very different.

What you want to avoid is a primordial stew of global variables with routines floating in it that need the variables (see the diagram, left). This kind of organisation makes it harder to use the routines in other programs and leads to more errors. The O-O approach (see the diagram, right) tries to keep data (and related functions) tucked away safely inside objects.

[globals bad, O-O good]

Generic programming

The Standard Library contains lots of routines to deal with common data structures - sets, lists, vectors, etc. The same routine name ("sort" for example) can be used on different types of objects, in the same way as the "+" sign can be used to add different kinds of numbers. This makes the higher level code easier to read. In the third talk we'll see some examples. To give you a flavour of the style, here's a short program that fills a vector with 1s, calculates partial sums (so the vector contains 1,2,...10) then randomly shuffles the values.

#include <vector> #include <algorithm> #include <numeric> using namespace std; int main() { vector<int> V1(10); fill(V1.begin(), V1.end(), 1); partial_sum(V1.begin(), V1.end(),V1.begin() ); random_shuffle(V1.begin(), V1.end()); }

Templates

You may not need to use these directly but the Standard Library uses them heavily. C++'s Templates are parameterized types - their arguments can be types (rather than values). They support generic programming by obviating the need for explicit switch statements to deal with various data types. An example should make this clearer. The following code swaps 2 integers in the usual C++ way

void swap (int& a, int& b) { int tmp = a; a = b; b = tmp; }

If you wanted to swap other types of variables (including those you've defined) you could copy this code, replacing int by (say) float. But in situations like this, where the code is potentially generic, templates can be used.

template <class T> void swap (T& a, T& b) { T tmp = a; a = b; b = tmp; }

Here the "T" name is arbitrary (like a formal parameter in a function definition). Note that the function is written much as before. When the compiler is now given the following code

int a = 3, b = 5; float f=7.5, g=9.5; swap (a, b); swap (f, g);

it will create a version ("instantiation") of swap for each type required.

Namespaces

Namespaces are like classes that have no functionality. They're a way of controlling visibility of variables. You can put an entity into a namespace by doing something like

namespace test { int i; }

then using test::i to access the variable. The command "using namespace test" will make all the entities in the namespace available for the rest of the unit that the statement is in, without the "test::" being necessary. With newer compilers standard library functions are in the std namespace. It's tempting to put "using namespace std;" at the top of each file to access all the standard routines, but this "pollutes the global namespace" with many routine names you'll never use, so consider using more specific commands like "using std::string"

Exceptions

In C every call had to be checked for error values - which could double the code size. C++ exceptions are an alternative to traditional techniques when they are insufficient, inelegant, and error-prone. Three keywords are involved

  • try - specifies an area of code where exceptions will operate
  • throw - causes an exceptional situation
  • catch - deals with the exceptional situation produced in the previous try clause.

When an exception is 'thrown' it will be 'caught' by the local "catch" clause if one exists, otherwise it will be passed up through the call hierarchy until a suitable "catch" clause is found. The default response to an exception is to terminate. It's not always clear when to use exception and when to use error codes

  • "In general (very roughly) exceptions are best for errors that you really can't continue on very well - you might recover but there is not a good way to finish the task that was asked. You can bypass all functions along the way and go back to one (or a few) places where you handle all bad errors. This can really clean up your code by separating the algorithm logic from the error logic." - Jeff Connelly.
  • "If a function will usually succeed, I'll throw an exception when it fails. That way, all the client code doesn't have to keep looking at return codes and comparing them. If a failure needs to be propagated up and handled by something other than the original caller, I'll throw an exception. Otherwise, I use error codes." - brougham2@yahoo.com

One situation where exceptions are useful is where you're using new a lot. Rather than checking each time to see if you've run out of memory, you can put the "main()" function in a try clause and write a "catch()" routine to tell the user what's happened. Another situation is when you have many levels of routine calls.

Learning C++

There are several ways - theoretical/practical; O-O/generic/procedural, etc. Older material tends to teach C++ by starting with C (conceptually, at least). Here are some options

More links are on our C++ page.