directory

  • Custom exception classes
    • The build process
    • Example: An exception class whose array subscript is out of bounds for Vec3D
  • Catch a variety of unrelated exceptions
    • Different exception capture
    • Catching derived exceptions
    • Order of exception handling
    • Example: multiple catch exception classes
    • Can the parameter types of a catch block be referenced without a type?

Custom exception classes

Custom exception classes are typically derived from Exception or its descendants. This way we can override the e.hat () function.

The build process

Here we take the example of building an exception class whose array subscript is out of bounds for a Vec3D class.

Class RangeException: public out_of_range; class RangeException: public out_of_range; size_t Dimension int index; RangeException(size_t dimension, int index) RangeException(size_t dimension, int index); Add a message to the parent class and initialize the parent class’s constructor on the constructor initializer list. RangeException () : out_of_range(” Vec index error “) {}; RangeException () : out_of_range(” Vec index error “) {};

Example: An exception class whose array subscript is out of bounds for Vec3D

Task1: Create a Vec3D class and use array to store vector members Create the RangeException class, Constructor RangeException(STD ::size_t dimension,const int index) Task3: Implements Vec3D::operator[](const int index) Task4: Create a Vec3D object in the main function and call [] to create the out-of-bounds problem, catch the exception and print the information in the exception.

RangeException.h:

#pragma once

#include <iostream>
#include <exception>
class RangeException : public std::exception {
private:
	std: :size_t dimension{ 3 };
	int index{ 0 };
public:
	RangeException (std: :size_t dimension,const int index) {
		this->dimension = dimension;
		this->index = index;
	}
	std: :size_t getDimension(a) {
		return dimension;
	}
	int getIndex(a) {
		returnindex; }};Copy the code

Vec3d. h: There is one point to note here: when the array subscript operator does not add &, it returns an rvalue. You cannot change the value of the array using the array subscript operator. If you add &, you get an lvalue.

#pragma once
#include <array>
#include "RangeException.h"
class Vec3D {
private:
	std: :array <double, 3> v{1.0.1.0.1.0};
public:
	Vec3D() = default;
	Vec3D(double x, double y, double z){
		v[0] = x;
		v[1] = y;
		v[2] = z;
	}
	double &operator [] (const int index) {
		if(index >=0 && index <= 2) {
			return v[index];
		}
		else {
			throw RangeException(3,index); }}};Copy the code

When the main function is:

#include <iostream>
#include "Vec3D.h"
using namespace std;

int main(a)
{
	Vec3D v1 {1.2.1.3.1.4};
	cout << v1[4];
	return 0;
}
Copy the code

As you can see, our custom exception is thrown.



Next we catch this exception:

Note that we are catching exceptions of the Exception base class type. Since RangeException inherits from Exception, we can catch it, but we are not overwriting what for Exception, so we will not print the exception.

#include <iostream>
#include <exception>
#include "Vec3D.h"
using namespace std;

int main(a)
{
	Vec3D v1 {1.2.1.3.1.4};
	try {
		cout << v1[4];
	}
	catch (exception & e) {
		cout << "Exception :" << e.what() << endl;
	}
	return 0;
}
Copy the code



Next we convert exception to RangeException and call the RangeException member function to query for exception information.

int main(a)
{
	Vec3D v1 {1.2.1.3.1.4};
	try {
		cout << v1[4];
	}
	catch (exception & e) {
		cout << "Exception :" << e.what() << endl;
		if (typeid(e) == typeid(RangeException)) {
			auto r = dynamic_cast<RangeException &>(e);
			cout << "Vector Dimension :" << r.getDimension() << endl;
			cout << "Index: " << r.getIndex() << endl; }}return 0;
}
Copy the code

Catch a variety of unrelated exceptions

Different exception capture

The code in the try block may throw different types of exceptions: notice that the objects thrown are the anonymous objects we created here.

class EA: public exception { };
class EB: public exception { };
class C {
public:
  void foo(int x) {
    if (x == 1)
      throw EA(a);else if (x == 2)
      throw EB();
  }
};
Copy the code

A catch block can catch only one exception:

int main(a) {
  C c { };
  try {
    c.foo(1);
    c.foo(2);
  } catch (EA& a) {
    cout << a.what() << endl;
  } catch (EB& b) {
    cout << b.what() << endl;
  }
Copy the code

Catching derived exceptions

Derived exception classes:

class MyException: public logic_error { };
Copy the code

If the catch parameter is a base-class exception type, it can catch either base-class objects or derived objects

try {
  throw MyException(a);// Throws a derived exception object
} catch (logic_error& e) {  The catch argument is a base-class exception, but can catch all derived exception objects
  MyException* p = dynamic_cast<MyException*>(&e); // If the pointer fails to turn, no exception will be thrown
  if(p ! =nullptr)
    cout << p->what() << endl;
  else
    cout << e.what() << endl;
}
Copy the code

Dynamic_cast (obj)

  1. If the transformation fails and NewType is a pointer type, nullPtr is returned.
  2. If the transition fails and NewType is a reference type, an exception of type STD :: BAD_cast is thrown

Order of exception handling

The correct order to catch exceptions:

The catch block of the derived class precedes the catch block of the base class

This is incorrect:

// (a)
try{... }catch (logic_error& e) {
  ...
} catch (MyException& e) {
  ...
}
Copy the code

Example: multiple catch exception classes

Task1: Add ZeroException based on Vec3D and RangeException classes Task3: Override operator/() to add a scalar division (vector divided by a number) to the Vec3D class and throw an exception when the divisor is 0.0. According to IEEE 754 rules: X > 0.0: x/0.0 = INF X < 0.0: x/0.0 = -INF 0.0/0.0 = NaN

Vec3D.h:

#pragma once

#include <array>
#include <string>
#include <cmath>
#include <limits>
#include "RangeException.h"
#include "ZeroException.h"
class Vec3D {
public:
	constexpr static std::size_t DIMENSION = 3;
	
private:
	std::array <double, DIMENSION> v{1.0.1.0.1.0};
	bool AreSame(double a, double b) {
		return std::fabs(a - b) < std::numeric_limits<double> : :epsilon(a); }public:
	Vec3D() = default;
	Vec3D(double x, double y, double z){
		v[0] = x;
		v[1] = y;
		v[2] = z;
	}
	double &operator [] (const int index) {
		if(index >=0 && index < DIMENSION) {
			return v[index];
		}
		else {
			throw RangeException(DIMENSION,index);
		}
	}
	Vec3D operator/ (const double divisor) {
		// Construct a copy of the current object
		Vec3D t(*this);
		if (AreSame(divisor,0.0))
			throw ZeroException(a);for (auto &i : t.v) {
			i /= divisor;
		}
		returnt; }};Copy the code

RangeException.h:

#pragma once

#include <iostream>
#include <exception>
class RangeException : public std::out_of_range {
private:
	std::size_t dimension{ 0 };
	int index{ 0 };
public:
	RangeException (std::size_t dimension,const int index) : out_of_range("index exceeds Vector dimension") {this->dimension = dimension;
		this->index = index;
	}
	std::size_t getDimension(a) {
		return dimension;
	}
	int getIndex(a) {
		returnindex; }};Copy the code

ZeroException.h:

#pragma once

#include <stdexcept>
#include <exception>

class ZeroException : public std::runtime_error {
public:
	ZeroException() : runtime_error("Means by 0.0") {};
	ZeroException(const char* msg) : runtime_error("Means by 0.0") {};
};
Copy the code

main.cpp:

#include <iostream>
#include <exception>
#include "RangeException.h"
#include "Vec3D.h"
using namespace std;

int main(a)
{
	Vec3D v1 {1.2.1.3.1.4};
	try {
		cout << (v1 / 0.0) [0] << endl;
	}
	catch (RangeException & e) {
		cout << "Exception :" << e.what() << endl;
		cout << "Vector Dimension :" << e.getDimension() << endl;
		cout << "Index: " << e.getIndex() << endl;
	}
	catch (ZeroException & e) {
		cout << "Exception :" << e.what() << endl;
	}
	return 0;
}
Copy the code

Effect:

Can the parameter types of a catch block be referenced without a type?

1. Can exception object parameters in parentheses catch () not reference types? 3. In multiple Exception catching code, if several parameters in the brackets of catch() belong to classes at different levels in a class inheritance chain, can the parameters in the brackets not reference types? Why is that?

1. The exception object argument in parentheses catch() can be used:

1Pointer to the objectcatch(Exception * o)

2Object referencecatch(Exception & o)

3An objectcatch(Exception  o) < >
Copy the code

2. In multiple exception catching code, if several parameters in the catch() brackets are objects of classes at different levels in an inheritance chain, the parameters in the brackets can be compiled without reference or pointer type.

What happens if the catch() argument is an object? :

1. Data member loss occurs

Polymorphic polymophic (dynamic binding of virtual functions) on inheritance chains can only occur using object references, pointer types. When a subclass object is assigned to a parent object, then release is between the object data members of a copy, if a subclass of some data members, more than the parent class data members will be more truncation, and approved by the assigned values of the parent class a virtual method can only be the parent class itself is called virtual method, can’t call a subclass of virtual method, this is a static binding.

2. Shallow copies occur

Object pointer data member of two exception class objects that point to the same new object in the heap. After copy construction occurs, the exception object used as an argument to the catch() function is destroyed (destructed and then reclaimed), and the object pointer to the exception object, whose member points to the address space of the new object in the heap, is reclaimed. But the exception object that is the parameter of the catch() function is still there, and needs to be destructed before the parameter leaves the scope of the catch block, and when it leaves the catch block, the parameter exception object POP is popped off the stack to free up space. When the parameter exception object is destructed, the address of the object that has already been reclaimed in the heap needs to be deleted first, so deleting a nonexistent address program will fail.

3. · An exception thrown as a throw should be an object that may be overridden by catch parameters and block data:

In the scope of the function, the exception object thrown as a local variable, the local variable is stored in the stack, the stack storage rule is first in and then out, and the size of the address order from high to low, and the stack frame addressing method to stack top (low address) to bottom (high address).

Throwing an exception is a gradual process that stops when a matching catch function is encountered and goes into the catch scope.

That is, the exception thrown hangs high on the stack, but the space of the exception object has been reclaimed (the object has popped off the stack).

However, the data members stored by the exception object still exist in the stack, and the data sequence written by the stack is high to low address. The address of the exception object thrown by the throw is the smallest address at the top of the stack, so it is difficult to be overwritten by other data.

When the address of the exception object thrown by the throw is captured in the catch scope, the data member of the exception object can be used.

If the formal parameter of a catch function is an object, the parameter object is also pushed to the stack for space allocation. This may cause the exception object thrown by a throw to be overwritten. Therefore, it is safer to use an object pointer or reference as the parameter of a catch() function