Object Oriented Features
C++ is often described as an object oriented language, but this is not
strictly true. It is really a procedural language (like C) with some
object oriented capabilities.
The key language feature is the concept of a class. A class is very
similar to a structure in C, but has some important differences and
enhancements:
1) A class is defined using
the keyword class.
2) A class may contain code
as well as data (not just pointers to functions)
3) Class members may be
declared as private or public, enabling key functionality to be hidden
(encapsulated)
4) A class is, in effect, a
new data type with the name of the declared class; the data and
possible operations on that data are defined
5) Operators may be defined
(overloaded) to manipulate the data in a class, which leads to
readable, intuitive code
6) Member functions may be
included that are automatically called when an object is created and
destroyed (constructors and destructors)
7) One class may be derived
from another, inheriting all of its characteristics, which may be
augmented or replaced
Templates
A much-vaunted feature of C++ is templates. This concept may be applied
to function or class definitions. The idea is simple: the programmer
can define the complete structure and logic of a class/function, but
leave the specification of data types (for parameters and internal
variables) open.
This acts as a "recipe" for the compiler to generate code/data when
the programmer instantiates a template (and indicates which data types
are required).
Here is a simple example of a function template:
template void swap(X& x, X& y)
{
X temp;
temp = x;
x = y;
y = temp;
}
This might be utilized like this:
int i, j;
float a, b;
...
swap(a, b); // instantiation #1
wap(i, j); // instantiation #2
Templates provide all programmers " desktop and embedded alike "
with a very useful capability to create highly reusable code. But
embedded developers always want to know the "cost" " what is the
overhead incurred by the use of templates?
In theory, no overhead would be anticipated, as a template does not
itself generate code " code is only created by the compiler when
required. But this is the cause of a problem: compilers only process
one module at a time, so the template instantiations are on a
per-module basis.
The result of this is the same code " i.e. a template instantiated
with an identical set of data types " might be generated in numerous
modules of the application. This may increase the memory footprint very
significantly. This may be of minimal concern to the desktop
programmer, but is likely to be of great importance to the embedded
system developer.
A possible solution to this problem " apart from simply
understanding what is happening " is to have tools which are optimized
for embedded work. One approach is to have a linker which can take
"hints" from the compiler and eliminate redundant (copies of) code.
Exception Handling
In almost any kind of software there exists the possibility for error
conditions to occur, which may be detected by the software. Often, this
requires some kind of "emergency exit" to some code to deal with the
error gracefully.
Unfortunately, structured programming languages (like C, C++ etc.)
do not conveniently handle such a possibility. The C++ Exception
Handling System (EHS) was created to address this problem.
The EHS is manifest in three additional C++ keywords: try, throw and
catch. A try block is used to control when exception handling is
active. A throw statement is used to instigate the processing of an
error (exception). And a catch block " of which there may be many, each
identified by an object type " contains the code to process the error.
Here is a simplistic example which shows how the EHS syntax works:
void scopy(char* str)
{
if (sizeof(store)+1 < strlen(str))
throw -1;
strcpy(store, str);
}
void get_string()
{
char buff[100];
cin >> buff;
scopy(buff);
}
main()
{
try
{
...
get_string();
...
}
}
catch (int err)
{
cout << "String too long!";
}
Although the EHS simplifies the coding required to deal with
emergency situations, it does come with a cost: extra code is generated
to enable throw statements to work correctly. An embedded developer is
always wary of extra overheads, so the size of this extra code should
be monitored.
There are two additional points about the EHS that embedded
developers need to appreciate:
First,
since the compiler has no way to know whether a particular piece of
code will be called from within a try block, the extra code will be
generated for all the application program modules. What is worse is
that some compilers have EHS enabled by default and it is the
programmer's responsibility to turn it off. So, if the EHS is not being
used, it is essential to check the options on the compiler to ensure
that no overhead is being incurred.
Second, the EHS is designed
to facilitate a selection of different responses to a wide variety of
error conditions. This makes complete sense for desktop applications.
However, many embedded applications have quite simple error handling
needs.
Often, a system reset is performed if a "difficult" error is
detected, as this is the best way to get back to a stable operating
state. It may quite reasonably be concluded that the EHS would be
overkill under these circumstances. However, there are two ways to use
it with lower overhead, which may tip the balance in its favor:
* If no matching catch statement is found when a throw is effected,
a library function terminate() is called. This function could be
replaced by a routine to perform a system reset.
* Just a generic catch handler may be provided, using this notation:
catch (...)
This will catch exceptions of any type and results in a measurably
lower code overhead.