 |
Department of Engineering |
 |
 |
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.