Recent C++ developments
Introduction
If you learnt C++ years ago, or you've been learning from old books, you may be unfamiliar with recent C++ developments. Some of these are advanced features that few current compilers support, but others are introduced early in some C++ courses nowadays.
Namespaces
Namespaces are like classes that have no functionality. They're a way of controlling scope. 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"
Casting
C++ is strict about converting from one type to another - much stricter than C is. You can use C-style casting but it's not recommended. Try to use the least dangerous (most specific) casting alternative from those below
- const_cast - used to remove the const qualifier.
- dynamic_cast - does careful, type-sensitive casting at run time. Returns 0 (or throws in the case of exceptions) when the conversion is not safe.
- reinterpret_cast - produces something that has the same bit pattern as the original.
- static_cast - doesn't examine the object it casts from at run time. This is fast if it's safe and - the compiler should tell you if it isn't safe. It lets you force a conversion that is already defined, or do a conversion from A to B where B to A is already defined. Can deal with void*.
For example, to convert from a const void * to a File_entry** (a common kind of thing to do in C) you need to do
File_entry**fe2= reinterpret_cast<File_entry**>(const_cast<void*>(entry2));
See Casting in C++ for details.
Standard Library
Recently standardised. Includes the STL. Useful even for simpler things. The following example creates, initialises and operates on an array without using explicit loops. Note that although it does a lot, it's not that hard to understand!
#include <iostream> #include <vector> #include <algorithm> #include <numeric> using namespace std; bool multiple_of_three(int num) {return ! (num%3);} void print_remainder(int num) {cout << num%3 << " ";} int add1(int num) {return num+1;} int main() { // create a vector big enough to hold 10 ints vector<int> v(10); fill(v.begin(), v.end(), 1); partial_sum(v.begin(), v.end(),v.begin() ); // v now contains 1....10 // Use transform to change values transform(v.begin(), v.end(), v.begin(),add1); // Use for_each if you're not changing values cout << "Print remainders when dividing 2...11 by 3" << endl; for_each(v.begin(), v.end(), print_remainder); cout << endl; // Random shuffle random_shuffle(v.begin(), v.end()); cout << "Remainders again, having shuffled" << endl; for_each(v.begin(), v.end(), print_remainder); cout << endl; // Use count_if to count how many elements have a particular property cout << count_if(v.begin(), v.end(), multiple_of_three) << " elements are a multiple of 3" << endl; }
See the Standard Template Library talk for examples.
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
- catch - deals with the exceptional situation produced in the previous try clause.
- throw - causes an exceptional situation
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.
There are many types of exception - they form a class hierarchy of their own. Here just a few will be introduced.
try { // code } // catch a standard exception catch(std::exception& e){ // order of the catch clauses matters; they're tried // in the given order } // catch all the other types catch(...) { // cleanup throw; // throw the exception up the hierarchy }
Here's an example that sooner or later will produce an exception.
try{ for(;;) new char[10000]; } catch(bad_alloc) { cerr << "No memory left"; }
"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.
"Other than that, I use them depending on common use. 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 propogated up and handled by something other than the original caller, I'll throw an exception. Otherwise, I use error codes." - brougham2@yahoo.com
Keywords
Some keywords have recently been introduced or have gained new meanings -
- explicit - when constructor shouldn't do type conversion.
- typename - when compiler needs to know that something's a type name, but it can't work it out for itself.
- exporting templates - Not currently supported by most compilers, so at the moment the source code of templates needs to be available to the code using templates.
Advanced features
- RTTI (run-time type identification)
#include <iostream> #include <typeinfo> #include <string> using namespace std; int main() { char c = 'A'; string s = "hello"; cout << "Type : " << typeid(c).name() << endl; cout << "Value : " << c << endl; cout << "Type : " << typeid(s).name() << endl; cout << "Value : " << s << endl; }
Future Developments
- "better" compilers - more conformant, better optimisation
- libraries based on the SL.
- Template Numerical Toolkit
- Generic Graph Component Library
- Graph Template Library
- Blitz++ (library for scientific computing)