Listing 15.13. newexcp.cpp
// newexcp.cpp -- the bad_alloc exception
#include
#include
#include
using namespace std;
struct Big
{
double stuff[20000];
};
int main()
{
Big * pb;
try {
cout << "Trying to get a big block of memory:\n";
pb = new Big[10000]; // 1,600,000,000 bytes
cout << "Got past the new request:\n";
}
catch (bad_alloc & ba)
{
cout << "Caught the exception!\n";
cout << ba.what() << endl;
exit(EXIT_FAILURE);
}
cout << "Memory successfully allocated\n";
pb[0].stuff[0] = 4;
cout << pb[0].stuff[0] << endl;
delete [] pb;
return 0;
}
Here’s the output for one system:
Trying to get a big block of memory:
Caught the exception!
std::bad_alloc
In this case, the what() method returns the string "std::bad_alloc".
If the program runs without allocation problems on your system, you can try increasing the amount of memory requested.
The Null Pointer and new
Much code was written when new (the old new) returned a null pointer upon failure. Some compilers handled the transition to the new new by letting the user set a flag or switch to choose which behavior she wanted. Currently, the standard provides for an alternative form of new that still returns a null pointer. Its use looks like this:
int * pi = new (std::nothrow) int;
int * pa = new (std::nowthrow) int[500];
Using this form, you could rewrite the core of Listing 15.13 this way:
Big * pb;
pb = new (std::nothrow) Big[10000]; // 1,600,000,000 bytes
if (pb == 0)
{
cout << "Could not allocate memory. Bye.\n";
exit(EXIT_FAILURE);
}
Exceptions, Classes, and Inheritance
Exceptions, classes, and inheritance interact in a couple ways. First, you can derive one exception class from another, as the standard C++ library does. Second, you can incorporate exceptions into classes by nesting exception class declarations inside a class definition. Third, such nested declarations can be inherited and can serve as base classes themselves.
Listing 15.14 starts us on the way to exploring some of these possibilities. This header file declares a rudimentary Sales class that holds a year value and an array of 12 monthly sales figures. The LabeledSales class derives from Sales and adds a member to hold a label for the data.
Listing 15.14. sales.h
// sales.h -- exceptions and inheritance
#include
#include
class Sales
{
public:
enum {MONTHS = 12}; // could be a static const
class bad_index : public std::logic_error
{
private:
int bi; // bad index value
public:
explicit bad_index(int ix,
const std::string & s = "Index error in Sales object\n");
int bi_val() const {return bi;}
virtual ~bad_index() throw() {}
};
explicit Sales(int yy = 0);
Sales(int yy, const double * gr, int n);
virtual ~Sales() { }
int Year() const { return year; }
virtual double operator[](int i) const;
virtual double & operator[](int i);
private:
double gross[MONTHS];
int year;
};
class LabeledSales : public Sales
{
public:
class nbad_index : public Sales::bad_index
{
private:
std::string lbl;
public:
nbad_index(const std::string & lb, int ix,
const std::string & s = "Index error in LabeledSales object\n");
const std::string & label_val() const {return lbl;}
virtual ~nbad_index() throw() {}
};
explicit LabeledSales(const std::string & lb = "none", int yy = 0);
LabeledSales(const std::string & lb, int yy, const double * gr, int n);
virtual ~LabeledSales() { }
const std::string & Label() const {return label;}
virtual double operator[](int i) const;
virtual double & operator[](int i);
private:
std::string label;
};
Let’s examine a few details of Listing 15.14. First, the symbolic constant MONTHS is in the protected section of Sales; this makes the value available to derived classes, such as LabeledSales.
Next, the bad_index class is nested in the public section of Sales; this makes the class available as a type to client catch blocks. Note that the outside world requires the type to be identified as Sales::bad_index. This class derives from the standard logic_error class. The bad_index class has the ability to store and report an out-of-bounds value for an array index.
The nbad_index class is nested in the public section of LabeledSales, making it available to client code as LabeledSales::nbad_index. It derives from bad_index, adding the ability to store and report the label of a LabeledSales object. Because bad_index derives from logic_error, nbad_index also ultimately derives from logic_error.
Both classes have overloaded operator[]() methods that are designed to access the individual array elements stored in an object and to throw an exception if an index is out of bounds.
Both the bad_index and nbad_index classes use the throw() exception specification. The reason is that both eventually inherit from the exception base class, and its virtual destructor uses the throw() exception specification. This is a C++98 feature; under C++11, the exception destructor doesn’t have an exception specification.