Programming Language Concepts Using C and C++/Exception Handling in C++

Similar to Java, exceptions in C++ are most often&mdash;not always!&mdash;objects of a class type. That is, instead of returning a value of a certain type an exception object may be returned from the function. However, one can also throw an exception object of a primitive type. The following is an example to this unlikely case.



! style="text-align:left;" | Example: Throwing an exception of a non-object type.
 * }

Other peculiarities of exceptions in C++ are related to the way they are specified and handled. In addition to listing the exact list of exceptions thrown from a function by means of an exception specification, one can optionally remove the specification and get the liberty of throwing any exception.



! style="text-align:left;" | Example: Exception specifications.
 * }

If we explicitly list the exceptions thrown from a function and it turns out that an unexpected exception&mdash;that is, an exception that is not listed in the specification&mdash;is thrown and not handled in the function call chain, a call to, defined in the C++ standard library, is made. In other words, detection of specification violations is carried out at run-time. If the control flow never reaches the point where the unexpected exception is thrown, program will run without a problem.

In line with its design philosophy C++ does not mandate that statements with a potential of throwing an exception be issued inside a  block. Similar to Java exceptions deriving from, C++ exceptions need not be guarded. In case we may be able to figure out that the exception never arises we can remove the  keywords and get cleaner code.



! style="text-align: left;" | Example: No mandatory try-catch blocks.
 * }

Exception Class
Queue_Exceptions

Note our exception class does not have any member fields. In other words, we have no means to identify details of the situation. All we know is we have a problem, nothing more! Although in our case we do not need any details about the nature of the problem, this is not always the case.

Take the factorial example for instance. We may want to pass the value of the argument that gave rise to the exceptional condition. This is equivalent to saying that we want to tell the difference between exception objects of the same class. As a matter of fact, we may formulate the problem as that of differentiating between objects of the same class, be that an exception object class or any other. We can do this simply by adding fields to the class definition.

Fact_Exceptions


 * }

Factorial.cxx


 * }

Note the only function of our class has been declared to be, which means we can invoke it without ever creating an instance of the class through the class scope operator. Similarly, one can define  data fields, which are shared by all instances of the class and there is no obligation to access these fields via objects of the class.

Interface
Queue

What follows is a forward class declaration. Its purpose is similar to that of forward declaration ala C: we declare our intention of using a class named  and defer its definition to some other place.

Note we cannot declare an object of this type. This is due to the fact that C++ does not let you declare variables to be of types whose definitions are not completed. Because compiler cannot figure out the amount of memory required for the object. However, we can declare variables to be pointers or references&mdash;read it as "constant pointers"&mdash;to such a class.

In some cases, it is convenient to allow a certain function/class to access the non-public members of a class without allowing access to the other functions/classes in the program. The friend mechanism in C++ allows a class to grant functions/classes free access to its non- members.

A friend declaration begins with the keyword. It may appear only within a class definition. In other words, it is the class that declares a function/class to be its friend, not the other way around. That is, you cannot simply declare a class as your friend and access its fields.

Since friends are not members of the class granting friendship, they are not affected by the,  , or   section in which they are declared within the class body. That is, friend declarations may appear anywhere in the class definition.

According to the following declaration, overloaded shift operator can freely access the internals of the   object, whose reference is passed as the second argument, as if they were.

Had we chosen to make the shift operator into an instance function we would not have attained our goal. Take the following example:



This statement will first print  and then   to the standard output file. We can reach the same effect by the following statements.

As a matter of fact, this is what takes place behind the scene. We can see what’s happening by applying the following transformations.

The shift message is sent twice: once to the object named  and once to the object returned by the first invocation of the appropriate function. This means we need to have a function signature where the value returned and the first argument are of the same type:  or. Knowing an instance function takes a pointer to an instance of the class being defined as its implicit first argument, we reach the conclusion that the shift operator cannot be an instance function of the  class. Way out of this is providing a friend declaration such as the following.

Note the types used in the exception specifications. The first function can abnormally return throwing a  object while the second one will return with a pointer to such an object. This should not come as a surprise. Unlike Java, which creates objects in the heap only, C++ lets you create your objects in all three regions&mdash;that is, the heap, the run-time stack, and the static data region. Since an exception object is basically a C++ object, you can create it in any data region you like.

Provided that you declare your exception handlers accordingly, there is not much of a difference between the following exception specifications. Handler of the first one will expect an object, while the second one will expect a pointer that points to some area in the heap.

Note all fields of the following class definition are. There are no functions to manipulate the objects, either. So, it looks like we need some magic for creating and manipulating an object of the class. Answer lies in ’s relation to  :   is tightly coupled to. A  object can exist only within the context of a   object. This fact is reflected in the friend declaration. Thanks to this declaration, we can [indirectly] manipulate a  object through operations on some   object.

Next statement declares the shift operator to be a friend to the  class. A similar declaration had been made in the  class, which means that one single function will have the privilege of peeking into the innards of two different classes.

Implementation
Queue.cxx

Our destructor, implicitly invoked by the programmer (through  in deallocating heap objects) or by the compiler-synthesized code (in the process of deallocating static and run-time stack objects), deletes all nodes in the queue and then proceeds with cleaning the room reserved for the fields. Had we forgotten to remove the items we would have ended up with the picture given below, which is actually the same picture we would have got without the destructor.



Note the shaded region denotes the memory returned to the allocator by the  operator itself, not the destructor. All queue nodes reachable only through the fields in the shaded region have now become garbage. So, we must remove all queue items when the queue is deleted, which is what we do in the destructor body.

Note also we do not write the code within a  block. Unlike Java, that’s OK with C++; you can choose to omit the  block if you think they will never happen. In this case, the number of removals is guaranteed to be as many as the number of items in the queue and this cannot give rise to any exceptional condition.

The following output operator definition makes use of both the  and the   classes. It first prints the length of the queue by using a private field of the  class and then outputs the contents of the corresponding queue by traversing each and every node, which are of   type. For this reason we had to make this function a friend to both classes.

Test Program
Queue_Test.cxx

Now that the argument to the handler points to some heap memory, we must destroy the region as soon as we are done with handling the exception. That’s what we do with the  operator inside the handler.

If we had preferred to pass an object instead of a pointer to an object, as we do in, there wouldn’t have been any need for such a clean-up activity; thanks to the code synthesized by the compiler, it would have been carried out automatically upon exit from the handler.

Observe we could have written the first statement of the handler as  This is OK because the sole function in our exception class is , which means we can call it through the class name.

Input-Output in C++
Input-output facilities in C++, a component of the standard library, are provided by means of the iostream library, which is implemented as a class hierarchy that makes use of both multiple and virtual inheritance. This hierarchy includes classes dealing with input from and/or output to user's terminal, disk files, and memory buffers.



The attributes of a particular stream type is somehow mangled in its name. For example,  stands for a file stream that we us as a source of input. Similarly,  is an in-memory buffer&mdash;a string object&mdash;stream that is used as a sink of output.

The Base Stream Class:
Whatever the name of the class being used might be it eventually derives from, the base class of the iostream library. This class contains the functionality common to all streams, such as accessor-mutator functions for manipulating state and format. In the former group are included the following functions:


 * : Returns the state of the current stream object, which can be any combination of the following:,  ,  , and.
 * :In addition to the already set flags, sets the state of the stream to . Note this function cannot be used to unset the flag values.
 * : Sets the state to the value passed in.
 * : Returns  if the last operation on the stream was successful.
 * : Returns  if the last operation on the stream found the end of file.
 * : Returns  if the last operation on the stream was not successful and no data was lost due to the operation.
 * : Returns  if the last operation on the stream was not successful and data was lost as a result of the operation.

For manipulating the format, we have


 * : Returns the padding character currently in use. The default character is space.
 * : Sets the padding character to  and returns the previous value.
 * : Returns the number of significant digits to be used for output of floating point numbers. The default value is 6.
 * : Sets precision to  and returns the previous value.
 * : Returns the output field width. Default value is 0, which means as many characters as necessary are used.
 * : Sets width to  and returns the previous value.
 * : Sets one of the flags, which are used to control the way output is produced.  can have one of the following: (for base value used in output of integral values) ,  ,  , (for displaying floating point values)  ,  , (for justifying text)  ,  ,  , (for displaying extra information)  ,  ,  ,  . As in the next four functions, this function returns the state that was in effect prior to the call.
 * : Clears the combination of flags passed in mask and then sets the flags passed in.
 * : Reverse of, this function makes sure the combination of flags passed in   is not set.
 * : Returns the current format state.
 * : Sets the format state to.

Input Streams
On top of the functionality listed in the previous section, all input streams in C++ provide support for the following functions.


 * : Overloaded versions of the shift-in (or extraction) operator are used to read in values of various types and can further be overloaded by the programmer. It can be used in a cascaded manner and in case the input operation is unsuccessful it returns, which means it can also be used in the context of a boolean expression.
 * : Returns the character under the read head and advances it by one.
 * : Like the previous function,  returns the character under the read head but doesn't move it. That is,   does not alter the stream contents.
 * : Cascaded version of, this function is equivalent to  . That is




 * : Reads a -terminated string into  . Length of this string depends on the second and third arguments, which hold the size of the buffer and the sentinel character, respectively. If   is scanned without reading the sentinel character,   is appended to the buffer and returned in the first argument. If the sentinel character is reached before filling in the buffer, the read head is left on the sentinel character and all that has been read up to that point with the terminating   is returned in the buffer.
 * : Similar to the previous function,  is used to read a  -terminated string into its first argument. However, in case the sentinel character is reached before the buffer is filled, the sentinel character is not left in the stream but read and discarded. Note the type of the first argument is a pointer to one of ,  , or.
 * : Reads  bytes into , unless the input ends first. If input ends before   bytes are read this function sets the   flag and returns the incomplete result.
 * : Corresponding to  of C, this function attempts to back up one character and replace the character that has been backed up with c. Note this operation is guaranteed to work only once. Consecutive uses of it may or may not work.
 * : Attempts to back up one character.
 * : This function reads and discards as many as  characters or all characters up to and including the.

Output Streams
Complementing the operations listed in the previous section are the operations applied on an output stream. Before we give a listing of these operations, we should mention one crucial point: in order for the output operations to take effect one of the following conditions must be met:


 * 1) An   manipulator or   is inserted into the stream.
 * 2) A   manipulator is inserted into or a   message is sent to the stream.
 * 3) The buffer attached to the stream is full.
 * 4) An   object tied to the stream performs an input operation. Tying two streams means their operations will be synchronized. A popular example is the  -  pair: before a message is sent to     is flushed. That is,


 * : Overloaded versions of the shift-out (or insertion) operator are used to write data of various types and can further be overloaded by the programmer. Like the extraction operator, it can be cascaded.
 * : Inserts  into the current stream.
 * : Inserts  characters of   into the current stream. Since a   object can be constructed from a , the first argument can also be a C-style character string.

Before moving on to file-oriented streams, we should mention that functionalities of  and   are combined in the   class, which derives from these two classes. That is, one can use the same stream for input and output at the same time.

File Input and Output
Using  and   one can read from and write to files. Since these classes inherit from the relevant stream classes&mdash; and , respectively&mdash;their instances can receive the messages given in the previous sections. In addition to these one can also use the following list.


 * , : Connects the stream being constructed to the disk file named  . The second and third arguments, which are optional, are used to specify the way the stream can be used. The third argument is specific to Unix-based operating systems and indicate the file protection bits. The second argument specifies how to open the disk file and can be a [reasonable] combination of the following:
 * : Opens the file for input and locates the read head at the beginning.
 * : Opens the file for output. While doing so, the file is truncated.
 * : Opens the file for output. File contents are not destroyed and each output operation inserts data to the end of the file.
 * : Treats the file content as raw data. In environments where  is mapped to a single byte this is not needed.
 * : Creates a stream object without connecting it to a disk file.
 * : Connects a previously constructed [disconnected] stream object to a disk file.
 * : Return the position of the file marker. The last letters,  for get and   for put, of these functions serve as a reminder of whether the file marker is a read head or a write head.
 * : These functions move the file marker&mdash;that is, the read or write head-to the absolute byte number specified by . seekg&mdash;read it as "seek to a new location for the next get"&mdash;affects the read head while seekp&mdash;read it as "seek to a new location for the next put"&mdash;affects the write head.
 * : Move by as many as  bytes relative to the location specified by , which can take one of the following values: [the beginning of the file]  , [the current file marker position]  , and [the end of the file].

As a closing remark of this handout we should mention the possibility of simultaneously reading from and writing to the same file. In such a case, we can construct a  object and use it to achieve our goal.