Beruflich Dokumente
Kultur Dokumente
1 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
arg_toobig = -2};
long fact(short n) {
if (n < 0) throw arg_neg;
if (n > MAX_ARG) throw arg_toobig;
if (n == 0 || n == 1) return 1;
else return (n * fact(n 1));
} // end of long fact(short)
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.
Example: Exception specifications.
// f1 can throw exceptions of types E1 and E2
void f1(...) throw(E1, E2);
// f2 does not throw any exceptions at all
void f2(...) throw();
// f3 can throw exceptions of any type. This might be a good choice
during the initial phases of a project.
void f3(...);
If we explicitly list the exceptions thrown from a function and it turns out that an unexpected
exceptionthat is, an exception that is not listed in the specificationis thrown and not handled in the
function call chain, a call to unexpected(), 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 try block. Similar to Java exceptions deriving from RuntimeException,
C++ exceptions need not be guarded. In case we may be able to figure out that the exception never arises
we can remove the try-catch keywords and get cleaner code.
Example: No mandatory try-catch blocks.
Rational divide_by_five(double x) {
Rational rat1 = Rational(x);
20-11-2014 13:07
2 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Contents
1 Exception Class
2 Module
2.1 Interface
2.2 Implementation
2.3 Test Program
3 Input-Output in C++
3.1 The Base Stream Class: ios
3.2 Input Streams
3.3 Output Streams
3.4 File Input and Output
4 Notes
Exception Class
Queue_Exceptions
1.
#ifndef QUEUE_EXCEPTIONS_HXX
2.
#define QUEUE_EXCEPTIONS_HXX
3.
4.
#include <iostream>
5.
using namespace std;
6.
7.
namespace CSE224 {
8.
namespace DS {
9.
namespace 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
20-11-2014 13:07
3 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
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
class Negative_Arg {
public:
void error(void) {
cerr << "Negative argument " << _arg_value << endl;
} // end of void error(void)
Negative_Arg(short arg) { _arg_value = arg; }
private:
short _arg_value;
} // end of class Negative_Arg
class TooBig_Arg { ... }
Factorial.cxx
...
long fact(short n) throw(Negative_Arg, TooBig_Arg) {
if (n < 0) throw Negative_Arg(n);
if (n > MAX_ARG) throw TooBig_Arg(n);
if (n == 0 || n == 1) return 1;
else return(n * fact(n 1));
} // end of long fact(short) throw(Negative_Arg, TooBig_Arg)
...
10.
class Queue_Empty {
11.
public:
Note the only function of our class has been declared to be static, which means we can invoke it without
ever creating an instance of the class through the class scope operator. Similarly, one can define static
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.
20-11-2014 13:07
4 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
12.
static void error(void) { cerr << "Queue Empty!!!" << endl; }
13.
}; // end of class Queue_Empty
14.
} // end of namespace Exceptions
15.
} // end of namespace DS
16.
} // end of namespace CSE224
17.
18.
#endif
Module
Interface
Queue
1.
#ifndef QUEUE_HXX
2.
#define QUEUE_HXX
3.
4.
#include <iostream>
5.
using namespace std;
6.
7.
#include "ds/exceptions/Queue_Exceptions"
8.
using namespace CSE224::DS::Exceptions;
9.
10.
namespace CSE224 {
11.
namespace DS {
20-11-2014 13:07
5 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
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 Queue_Node 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 referencesread it
as "constant pointers"to such a class.
12.
class Queue_Node;
13.
class Queue {
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-public members.
A friend declaration begins with the keyword friend. 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 public,
protected, or private 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
Queue object, whose reference is passed as the second argument, as if they were public.
Had we chosen to make the shift operator into an instance function we would not have attained our goal.
Take the following example:
cout << q1 << q2;
This statement will first print q1 and then q2 to the standard output file. We can reach the same effect by
the following statements.
cout << q1;
cout << q2;
As a matter of fact, this is what takes place behind the scene. We can see whats happening by applying the
following transformations.
cout << q1 << q2;
cout.operator<<(q1).operator<<(q2);
x.operator<<(q2);
The shift message is sent twice: once to the object named cout and once to the object returned by the first
invocation of the appropriate function (x). This means we need to have a function signature where the value
returned and the first argument are of the same type: ostream or ostream&. 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 Queue class. Way out of this is
20-11-2014 13:07
6 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
14.
friend ostream& operator<<(ostream&, const Queue&);
15.
public:
16.
Queue(void) : _front(NULL), _rear(NULL), _size(0) { }
17.
Queue(const Queue&);
18.
~Queue(void);
19.
Queue& operator=(const Queue&);
20.
bool operator==(const Queue&);
Note the types used in the exception specifications. The first function can abnormally return throwing a
Queue_Empty 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 regionsthat 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.[1] 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.[2]
21.
double peek(void) throw(Queue_Empty);
22.
double remove(void) throw(Queue_Empty*);
23.
void insert(double);
24.
bool empty(void);
25.
private:
26.
Queue_Node *_front, *_rear;
27.
unsigned int _size;
28.
}; // end of class Queue
20-11-2014 13:07
7 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Note all fields of the following class definition are private. 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 Queue_Nodes relation to Queue: Queue_Node is tightly coupled to Queue. A
Queue_Node object can exist only within the context of a Queue object. This fact is reflected in the friend
declaration. Thanks to this declaration, we can [indirectly] manipulate a Queue_Node object through
operations on some Queue object.
30.
class Queue_Node {
31.
friend class Queue;
Next statement declares the shift operator to be a friend to the Queue_Node class. A similar declaration
had been made in the Queue class, which means that one single function will have the privilege of peeking
into the innards of two different classes.
32.
friend ostream& operator<<(ostream&, const Queue&);
33.
private:
34.
double _item;
35.
Queue_Node *_next;
36.
Queue_Node(double val = 0) : _item(val), _next(NULL) { }
37.
}; // end of class Queue_Node
38.
} // end of namespace DS
39.
} // end of namespace CSE224
40.
41.
#endif
Implementation
Queue.cxx
1.
20-11-2014 13:07
8 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
#include <iomanip>
2.
#include <iostream>
3.
using namespace std;
4.
5.
#include "ds/Queue"
6.
#include "ds/exceptions/Queue_Exceptions"
7.
using namespace CSE224::DS::Exceptions;
8.
9.
namespace CSE224 {
10.
namespace DS {
11.
Queue::
12.
Queue(const Queue& rhs) : _front(NULL), _rear(NULL), _size(0) {
13.
Queue_Node *ptr = rhs._front;
14.
for(unsigned int i = 0; i < rhs._size; i++) {
15.
this->insert(ptr->_item);
16.
ptr = ptr->_next;
17.
} // end of for(unsigned int i = 0; i < rhs._size; i++)
18.
} // end of copy constructor
Our destructor, implicitly invoked by the programmer (through delete 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.
20-11-2014 13:07
9 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Note the shaded region denotes the memory returned to the allocator by the delete operator itself, not the
destructor.[3] 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 try-catch block. Unlike Java, thats OK with C++; you can
choose to omit the try-catch 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.
20.
Queue::
21.
~Queue(void) {
22.
unsigned int size = _size;
23.
for(unsigned int i = 0; i < size; i++) remove();
24.
} // end of destructor
25.
26.
Queue& Queue::
27.
operator=(const Queue& rhs) {
28.
if (this == &rhs) return (*this);
29.
30.
for(unsigned int i = _size; i > 0; i--) remove();
31.
32.
Queue_Node *ptr = rhs._front;
20-11-2014 13:07
10 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
33.
for(unsigned int i = 0; i < rhs._size; i++) {
34.
this->insert(ptr->_item);
35.
ptr = ptr->_next;
36.
} // end of for(unsigned int i = 0; i < rhs._size; i++)
37.
38.
if (rhs._size == 0) {
39.
_front = _rear = NULL;
40.
_size = 0;
41.
return(*this);
42.
} // end of if(rhs._size == 0)
43.
44.
return (*this);
45.
} // end of assignment operator
46.
47.
bool Queue::
48.
operator==(const Queue& rhs) {
49.
if (_size != rhs._size) return false;
50.
if (_size == 0 || this == &rhs) return true;
51.
52.
Queue_Node *ptr = _front;
53.
Queue_Node *ptr_rhs = rhs._front;
54.
55.
for (unsigned int i = 0; i < _size; i++) {
56.
if (ptr->_item != ptr_rhs->_item)
57.
return false;
58.
ptr = ptr->_next;
59.
ptr_rhs = ptr_rhs->_next;
60.
} // end of for(unsigned int i = 0; i < _size; i++)
20-11-2014 13:07
11 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
61.
62.
return true;
63.
} // end of equality-test operator
64.
65.
double Queue::
66.
peek(void) throw(Queue_Empty) {
67.
if (empty()) throw Queue_Empty();
68.
69.
return(_front->_item);
70.
} // end of double Queue::peek(void)
71.
72.
double Queue::
73.
remove(void) throw(Queue_Empty*) {
74.
if (empty()) throw new Queue_Empty();
75.
76.
double ret_val = _front->_item;
77.
Queue_Node *temp_node = _front;
78.
79.
if (_front == _rear) _front = _rear = NULL;
80.
else _front = _front->_next;
81.
82.
delete temp_node;
83.
_size--;
84.
85.
return ret_val;
86.
} // end of double Queue::remove(void)
87.
88.
20-11-2014 13:07
12 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
void Queue::
89.
insert(double value) {
90.
Queue_Node *new_node = new Queue_Node(value);
91.
92.
if (empty()) {
93.
_front = _rear = new_node;
94.
_size = 1;
95.
return;
96.
} // end of if (empty())
97.
98.
_rear->_next = new_node;
99.
_rear = _rear->_next;
100.
_size++;
101.
} // end of void Queue::insert(double)
102.
103.
bool Queue::
104.
empty(void) { return (_size == 0); }
The following output operator definition makes use of both the Queue and the Queue_Node classes. It first
prints the length of the queue by using a private field of the Queue class and then outputs the contents of
the corresponding queue by traversing each and every node, which are of Queue_Node type. For this
reason we had to make this function a friend to both classes.
106.
ostream& operator<<(ostream& os, const Queue& rhs) {
107.
os << "( " << rhs._size << " )";
108.
109.
if (rhs._size == 0) {
110.
os << endl;
111.
20-11-2014 13:07
13 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
return(os);
112.
} // end of if (rhs._size == 0)
113.
114.
os << "(front: ";
115.
Queue_Node *iter = rhs._front;
116.
while(iter != NULL) {
117.
os << iter->_item << " ";
118.
iter = iter->_next;
119.
} // end of while(*iter != NULL)
120.
os << " :rear )\n";
121.
122.
return(os);
123.
} // end of ostream& operator<<(ostream&, const Queue&)
124.
} // end of namespace DS
125.
} // end of namespace CSE224
Test Program
Queue_Test.cxx
1.
#include <fstream>
2.
#include <string>
3.
using namespace std;
4.
5.
#include "ds/Queue"
6.
using namespace CSE224::DS;
7.
8.
#include "ds/exceptions/Queue_Exceptions"
20-11-2014 13:07
14 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
9.
using namespace CSE224::DS::Exceptions;
10.
11.
int main(void) {
12.
Queue q1;
13.
string fname("Queue_Test.input");
14.
ifstream infile(fname.c_str());
15.
16.
if (!infile) {
17.
cout << "Unable to open file: " << fname << endl;
18.
return 1;
19.
} // end of if(!infile)
Now that the argument to the handler (q) points to some heap memory, we must destroy the region as soon
as we are done with handling the exception. Thats what we do with the delete operator inside the handler.
If we had preferred to pass an object instead of a pointer to an object, as we do in peek, there wouldnt
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 Queue_Empty::error(); This is
OK because the sole function in our exception class is static, which means we can call it through the class
name.
21.
try { q1.remove(); }
22.
catch(Queue_Empty* q) { q->error(); delete q; }
23.
24.
for (int i = 0; i < 10; i++) {
25.
double val;
26.
infile >> val;
27.
q1.insert(val);
28.
} // end of for(int i = 0; i < 10; i++)
20-11-2014 13:07
15 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
29.
infile.close();
30.
31.
cout << q1;
32.
Queue q2 = q1;
33.
34.
cout << "Queue 1: " << q1;
35.
cout << "Queue 2: " << q2;
36.
37.
if (q1 == q2) cout << "OK" << endl;
38.
else cout << "Something wrong with equality testing!" << endl;
39.
40.
q2.remove(); q2.remove();
41.
cout << "Queue 2: " << q2;
42.
if (q1 == q2) cout << "Something wrong with equality testing!" << endl;
43.
else cout << "OK" << endl;
44.
45.
return(0);
46.
} // end of int main(void)
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.
20-11-2014 13:07
16 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
The attributes of a particular stream type is somehow mangled in its name. For example, ifstream stands
for a file stream that we us as a source of input. Similarly, ostringstream is an in-memory buffera
string objectstream that is used as a sink of output.
20-11-2014 13:07
17 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
fmtflags setf(fmtflags flag, fmtflags mask): Clears the combination of flags passed
in mask and then sets the flags passed in flag.
fmtflags unsetf(fmtflags flag): Reverse of setf, this function makes sure the
combination of flags passed in flag is not set.
fmtflags flags(void) const: Returns the current format state.
fmtflags flags(fmtflags new_flags): Sets the format state to new_flags.
Input Streams
On top of the functionality listed in the previous section, all input streams in C++ provide support for the
following functions.
istream& operator>>(type data): 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 false,
which means it can also be used in the context of a boolean expression.
int get(void): Returns the character under the read head and advances it by one.
int peek(void): Like the previous function, peek returns the character under the read head but
doesn't move it. That is, peek does not alter the stream contents.
istream& get(char& c): Cascaded version of get(void), this function is equivalent to
operator>>(char&). That is
in_str.get(c1).get(c2).get(c3); in_str >> c1 >> c2 >> c3;
istream& get(char* str, streamsize len, char delim = '\n'): Reads a
null-terminated string into str. Length of this string depends on the second and third arguments,
which hold the size of the buffer and the sentinel character, respectively. If len - 1 is scanned
without reading the sentinel character, '\0' 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 '\0' is returned in
the buffer.
istream& getline(ctype* str, streamsize len, char delim = '\n'): Similar to
the previous function, getline is used to read a null-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
char, unsigned char, or signed char.
istream& read(void* buf, streamsize len): Reads len bytes into buf, unless the
input ends first. If input ends before len bytes are read this function sets the ios::fail flag and
returns the incomplete result.
istream& putback(char c): Corresponding to ungetc(char) 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.
istream& unget(void): Attempts to back up one character.
istream& ignore(streamsize len, char delim = traits::eof): This function
reads and discards as many as len characters or all characters up to and including the delim.
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 endl manipulator or '\n' is inserted into the stream.
2. A flush manipulator is inserted into or a flush() message is sent to the stream.
20-11-2014 13:07
18 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Before moving on to file-oriented streams, we should mention that functionalities of istream and
ostream are combined in the iostream class, which derives from these two classes. That is, one can use
the same stream for input and output at the same time.
20-11-2014 13:07
19 of 19
http://en.wikibooks.org/wiki/Programming_Language_Concepts_Using...
Notes
1. Actually, there is a difference. Be that a plain object or an exception object, an object created in the
heap is managed by the programmer and must be freed by her
2. As a matter of fact, you can pass a pointer to some area in other parts of the address space such as
the static data or run-time stack regions. But then how are you going to decide whether to free the
region or not? If it points to some place in the heap, it is the programmers responsibility and she must
free the object; if the pointed object is not in the heap its lifetime will be managed by the compiler.
Wed better be more deterministic and create all such objects in the heap or have the handler accept
an extra argument. Or yet better, choose to pass objects, not pointers to object.
3. Observe this is basically the same region that would have been returned with free in the case of a
malloced heap object: the region pointed to by the pointer. This semblance leads us to an informal
definition: delete operator is implicit invocation of the destructor plus free.
Retrieved from "http://en.wikibooks.org
/w/index.php?title=Programming_Language_Concepts_Using_C_and_C%2B%2B
/Exception_Handling_in_C%2B%2B&oldid=2718705"
20-11-2014 13:07