C and C + + exceptions and handling methods, including inheritance, construction and deconstruction exception handling

C and C + + exceptions and handling methods

C language error handling method

1. Return value (if else statement judgment error)

//Open, read and write pseudocode of source file src and target file dst
int copy(const char* src,const char* dst)
{
	open(src);//open the source file
		return -1;
	open(dst);//open the target file
		return -2;
	read(src,buf);//read source file
		return -3;
	write(dst,buf);//Write target file
		return -4;
}
//According to different abnormal return values, different processing is carried out
void fun()
{
	int ret;
	ret = copy(src,dst);
	if(ret == -1)
	{
	}
	else if(ret == -2)
	{
	}
	else if(ret == -3)
	{
	}
	else if(ret == -4)
	{
	}
}

2. errno (linux system call)

error
1. error description

  1. The program will produce various kinds of errors in the process of running. We can assign a unique number to each type of error, just like assigning a student number to the students in the class. In C language, we call this error code.
  2. < errno. H > is a header file for checking errors in the C standard library. Errno is used to save the final error code. It is a macro. After being expanded, it is an int type data (which can be considered as a global variable in a single threaded program), and the current program has read and write permissions.
  3. When an exception occurs to the linux C api function, the errno variable (including errno. H) is usually assigned an integer value. Different values represent different meanings. You can infer the cause of the error by looking at the value.

2. Error code: EDOM,ERANGE

macro explain
EDOM Domain error. Some mathematical functions are meaningful only for a range of values, which we call domain. For example, the sqrt() function can only square root non negative numbers. If we pass a negative number to sqrt(), sqrt() will set errno to EDOM.
ERANGE Range error. A variable can represent a limited range of values, too large or too small values have the risk of spillover. For example, pow() is used to find the power of a number, and its result is likely to exceed the representation range of floating-point variables; for example, strtod() is used to convert strings to floating-point numbers, and it may also exceed the range. In these cases, errno is set to ERANGE.

3. Use of errno

  • 1. When the program is just started, errno is set to 0; during the running process of the program, any function error may change the value of errno to a non-zero value to inform the > user that a specific type of error has occurred.

The functions in the standard library will only set errno to a non-zero value indicating the specific error type, and will not set errno back to zero.

  • 2. Setting the value of errno again will overwrite the previous value of errno. If you want to know what error has happened to the current function, you must read the value of errno immediately after the function call, because there is still a possibility of error in the later function, so you will modify the value of errno again, and change the previous value of errno If the value of is overridden, we will never know what happened to the previous function.
  • 3. It is best to reset the value of errno to 0 before calling the function. Because the previous function may have set the value of errno. If we do not set errno back to 0, we will read a non-zero value even if the current function does not make an error, which is obviously not reliable.

The error code is just a number with no additional structure. To get specific error information, There are generally two options:

Use perror() to print the error information (text) to the standard output device;
Use strerror() to convert the error code to the corresponding text information.

void perror(const char *s);
The perror function is declared in < stdio. H >
Output parameter string s, colon, space in turn, error information and newline character determined by errno.

char *strerror(int enum);
strerror stay<string.h>Statement in

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <math.h>

int main()
{
  errno = 0;
  sqrt(-1);
  perror("Square parameter error");

  puts(strerror(EDOM));
  puts(strerror(errno));

  return 0;
}

3. goto statement (local jump in function)

4,setjmp,longjmp

  1. In general, the function will return to the place where it is called to continue execution after execution. If you want to jump directly from the function to another function, you can use the non local jump function provided in < setjmp. H >.
  2. The main functions in < setjmp. H > are setjmp and longjmp. Setjmp macro marks a location in the program, and then longjmp jumps to this location. This mechanism is mainly used for error handling. The prototypes of these two functions are:

int setjmp(jmpbuf env);
The parameter of setjmp macro call is JMP_ The buff type is also defined in * * < setjmp. H >. Setjmp saves the current environment, including the pointer to the currently drawn, to the variable in order to invoke it in longjmp and return to 0..
void longjmp(jmp_buf env, int val);
Use the longjmp macro to go back to the place marked by the setjmp macro, and call the same JMP as setjmp_ BUF variable, longjmp first according to jmp_buf recovers the environment, and then returns from setjmp. The returned value is the value of val (if val is 0, setjmp returns 1). val is actually the flag of the location where the jump occurs. You can judge which longjmp * * the current code is jumping from according to this value.

#include <setjmp.h>
#include <stdio.h>

static jmp_buf env;

void f1(void);
void f2(void);

int main()
{
  int ret;
  ret = setjmp(env);
  printf("setjmp returned %d\n", ret);
  if (ret != 0)
  {
    printf("Program terminates: longjmp called\n");
    return 0;
  }
  f1();
  printf("Program terminates normally\n");
  return 0;
}

void f1(void)
{
  printf("f1 begins\n");
  f2();
  printf("f2 returns");
}

void f2(void)
{
  printf("f2 begins\n");
  longjmp(env, 1);
  printf("f2 returns\n");
}

5,assert.h

Assert function declaration in < assert. H >
void assert(int expression);
The assert function is implemented in the form of a macro. The parameter expression is an expression that should normally be true. When the assert function is executed, the value of expression will be detected. If the result is 0 (false), the assert function will output the information of assertion failure to the standard error stream (including the parameters of the assertion function, the file name of the assertion function and the line number of the assertion function), and call the abort function to terminate the program.

#include <stdio.h>
#include <assert.h>

int main()
{
  int x;
  printf("Please enter a positive integer:");
  scanf("%d", &x);

  assert(x > 0);

  printf("The number entered meets the requirements!\n");

  return 0;
}

C + + exception

Program error, exception (syntax, throw, catch, propagate), stack expansion

Program error

Compilation error, that is, syntax error. The program cannot be generated to run.
Runtime error
1. Unexpected logic error
2. Predictable operational exceptions
For example:

1. Dynamic space allocation may not succeed
2. Open file may fail
3. Denominator may be 0 in division operation
4. Integer multiplication may overflow
5. Array out of bounds

1. Exception syntax

throw expression;

try
{
    //try statement block
}

catch(type1  parameter1)
{
    //Exception handling for type 1
}

catch (type2  parameter2)
{
    //Exception handling for type 2
}

catch (type n  parameter n)
{
    //Exception handling for type n
}

2. Exception thrown

1. You can throw built-in type exceptions or custom type exceptions
2. throw throws a class object and calls the copy constructor
3. The local object created before the exception is destroyed, which is called stack expansion

3. Exception capture

1. An exception handler generally catches only one type of exception
2. The parameter type of the exception handler is the same as the type of exception thrown

... Indicates that any exception can be caught

#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
    MyException(const char *message)
        : message_(message)
    {
        cout << "MyException ..." << endl;
    }
    MyException(const MyException &other) : message_(other.message_)
    {
        cout << "Copy MyException ..." << endl;
    }
    ~MyException()
    {
        cout << "~MyException" << endl;
    }

    const char *what() const
    {
        return message_.c_str();
    }
private:
    string message_;
};
double Divide(double a, double b)
{
    if (b == 0.0)
    {
        MyException e("division by zero");
        throw e;
    }
    else
        return a / b;
}
int main(void)
{
    try
    {
        cout << Divide(5.0, 0.0) << endl;
    }
    catch (MyException &e)
    {
        cout << e.what() << endl;
    }
}


The program defines an exception type MyException. From the output, we can see that a MyException object e is constructed first in the Divide function and the constructor is called, because e is a local object that needs to be deconstructed (stack expansion). Before deconstruction, we call the copy constructor to construct another object. This object will be referenced by catch, and finally this object will be deconstructed at the end of catch.

//If throw 1 is assumed, and there is no corresponding catch (int), even if there is a catch (double), it cannot be caught, and no type conversion will be done,
#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};
double Divide(double a, double b)
{
	if (b == 0.0)
	{
		throw 1;		//throw an integer variable
	}
	else
		return a / b;
}
int main(void)
{
	try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)// ERROR, throw 1. There is no way for custom type e to capture integer variables
	{
		cout << e.what() << endl;
	}
	
}

//Solution 1: catch (int)

	try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)
	{
		cout << e.what() << endl;
	}
	catch (int)
	{
	  cout<<"int exception ..."<<endl;
	}

//Solution 2: catch ( )Caught Indicates that any exception can be caught.

try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)
	{
		cout << e.what() << endl;
	}
	/*catch (int)
	{
	  cout<<"int exception ..."<<endl;
	}*/
	catch (...)
	{
		cout << "catch a exception ..." << endl;
	}

If no local object is constructed, throw directly, such as throw MyException("division by zero"); then the copy constructor will not be called, only one object exists, which is deconstructed at the end of catch.

#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};
double Divide(double a, double b)
{
	if (b == 0.0)
	{
		throw MyException("division by zero");
	}
	else
		return a / b;
}
int main(void)
{
	try
	{
		cout << Divide(5.0, 0.0) << endl;
	}
	catch (MyException &e)
	{
		cout << e.what() << endl;
	}
	catch (int)
	{
	  cout<<"int exception ..."<<endl;
	}
}

4. Abnormal propagation

1. try blocks can be nested
2. The program looks for the matching exception handler in order, and the thrown exception will be caught by the first exception handler of the matching type
If no suitable exception handler is found behind the inner try block, the exception will propagate outwards to the catch block behind the outer try block
3. Exceptions not caught will call the terminate function, which by default calls abort to terminate the execution of the program
4. You can use set_ The terminate function specifies the function that the terminate function will call before calling abort

//If no suitable exception handler is found behind the inner try block, the exception will propagate outwards to the catch block behind the outer try block
#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

int main(void)
{
	try
	{
		try
		{
			throw MyException("test exception");
		}
		catch (int)
		{
			cout << "Inner..." << endl;
			cout << "catch a int exception" << endl;
		}
	}
	catch (MyException &e)
    {
        cout << "Outer ..." << endl;
        cout << e.what() << endl;
    }
	return 0;
}

//If you can catch it internally and want to spread it out, you need to throw an exception again
try
    {
        try
        {
            throw MyException("test exception");
        }
        catch (int)
        {
            cout << "Inner ..." << endl;
            cout << "catch a int exception" << endl;
        }
        catch (MyException& e)
        {
          cout<<"Inner ..."<<endl;
          cout<<e.what()<<endl;
          throw e;
        }
    }
    catch (MyException &e)
    {
        cout << "Outer ..." << endl;
        cout << e.what() << endl;
    }

**Exceptions that are not caught will be called terminate Function, terminate Function default call abort Execution of termination procedure**
#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

int main(void)
{
	try
	{
		try
		{
			throw MyException("test exception");
		}
		catch (int)
		{
			cout << "Inner..." << endl;
			cout << "catch a int exception" << endl;
		}
	}
	catch (int)
	{
		cout << "Inner..." << endl;
		cout << "catch a int exception" << endl;
	}
	return 0;
}


The above dialog is called by the abort() function. You can directly add abort() to main() to see the termination dialog.

//You can use set_ The terminate function specifies the function that the terminate function will call before calling abort
#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

void MyTerminate()
{
	cout << "MyTerminate ..." << endl;
}

int main(void)
{
	set_terminate(MyTerminate);
	try
	{
		try
		{
			throw MyException("test exception");
		}
		catch (int)
		{
			cout << "Inner..." << endl;
			cout << "catch a int exception" << endl;
		}
	}
	catch (int)
	{
		cout << "Outer ..." << endl;
		cout << "catch a int exception" << endl;
	}
	return 0;
}

5. Stack expansion

1. Look up along the nested call link until a catch clause is found for the exception. This process is called stack expansion.

  • Call destructor for local object
  • Destructors should never throw exceptions
    The destructor will be executed during the stack expansion. When the destructor is executed, the exception that has been thrown has not been handled. If the destructor throws a new exception during this process, the terminate function of the standard library will be called.
  • Exceptions and constructors
    An exception can be thrown in the constructor. If an exception is thrown in a constructor function, it is possible that the object is only partially constructed. Even if the object is only partially constructed, ensure that the constructed members are destroyed. (if the member is pointer p, because the destructor will not be called, general delete p will not be executed; it is likely to cause memory leak)
    1. Call destructor for local object
//Call destructor for local object
#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

class Test
{
public:
	Test()
	{
		cout << "Test()...... "<< endl;
	}
	Test(const Test& other)
	{
		cout << "Copy Test()...... " << endl;
	}
	~Test()
	{
		cout << "~Test()...... " << endl;
	}
};

int main(void)
{
	try
	{
		Test t;
		throw MyException("test exception");
	}
	catch (MyException &e)
    {
        cout << "Outer ..." << endl;
        cout << e.what() << endl;
    }
	return 0;
}


2. Destructors should never throw exceptions

#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

class Test
{
public:
	Test()
	{
		cout << "Test()...... "<< endl;
	}
	Test(const Test& other)
	{
		cout << "Copy Test()...... " << endl;
	}
	~Test()
	{
		cout << "~Test()...... " << endl;
	}
};
class Obj
{
public:
	Obj()
	{
		cout << "Obj()...... " << endl;
	}
	Obj(const Obj& other)
	{
		cout << "Copy Obj()...... " << endl;
	}
	~Obj()
	{
		cout << "~Obj()...... " << endl;
	}
};
class Test2
{
public:
	Test2()
	{
		cout << "Test2()...... " << endl;
	}
	Test2(const Test2& other)
	{
		cout << "Copy Test2()...... " << endl;
	}
	~Test2()
	{
		cout << "~Test2()...... " << endl;
		throw 2;
	}
};

int main(void)
{
	try
	{
		Test2 t2;
		throw MyException("Test2 exception1");//Throwing an exception calls Test2's destructor
	}
	catch (MyException &e)
    {
        cout << e.what() << endl;
    }
	catch (int)
	{
		cout <<"Catch a exception..." << endl;
	}
	return 0;
}


**throw MyException("Test2 exception1"); * * throwing an exception will call Test2's destructor. At this time, Test2's destructor has not finished processing and throws a new exception in the destructor

~Test2()
	{
		cout << "~Test2()...... " << endl;
		throw 2;
	}

The exception has not been captured by catch (int), and the program has called the terminate function ahead of time.

3. An exception can be thrown in the constructor. If an exception is thrown in a constructor function, it is possible that the object is only partially constructed.
Even if the object is only partially constructed, ensure that the constructed members are destroyed.

#include <iostream>
#include <string>
using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	~MyException()
	{
		cout << "~MyException" << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};
class Obj
{
public:
	Obj()
	{
		cout << "Obj()...... " << endl;
	}
	Obj(const Obj& other)
	{
		cout << "Copy Obj()...... " << endl;
	}
	~Obj()
	{
		cout << "~Obj()...... " << endl;
	}
};
class Test2
{
public:
	Test2()
	{
		cout << "Test2()...... " << endl;
		throw MyException("test2 exception");
	}
	Test2(const Test2& other)
	{
		cout << "Copy Test2()...... " << endl;
	}
	~Test2()
	{
		cout << "~Test2()...... " << endl;
	}
private:
	Obj obj_;
};

int main(void)
{
	try
	{
		Test2 t2;
	}
	catch (MyException &e)
    {
        cout << e.what() << endl;
    }
	return 0;
}


Procedure description:
Here, a member of obj object is defined in Test2. When defining t2 object, the constructor of object member is called first according to the calling order of constructor, so obj_ After the construction is successful, the Test2 constructor is called, and an exception throw MyException("test2 exception") is thrown;. But obj_ The destructor of is called, so the constructed object is guaranteed to be destroyed.
be careful:
If the member is pointer P, because the destructor will not be called, it will not execute the general delete p; it is likely to cause memory leakage

class Test2
{
public:
	Test2()
	{
		obj_ = new Obj;
		cout << "Test2()...... " << endl;
		throw MyException("test2 exception");
	}
	Test2(const Test2& other)
	{
		cout << "Copy Test2()...... " << endl;
	}
	~Test2()
	{
		delete obj_;
		cout << "~Test2()...... " << endl;
	}
private:
	Obj* obj_;//Object pointer defined
};

Here, the object pointer is defined in Test2. Now * * obj_ **The construction is completed, but an exception is thrown, so the destructor is not called, and there is a memory leak.

6. Exceptions and inheritance

If the exception type is a class of C + +, and the class has its base class, you should put the error handler of the derived class first and the error handler of the base class second

#include <iostream>
#include <string>

using namespace std;

class MyException
{
public:
    MyException(const char *message)
        : message_(message)
    {
        cout << "MyException ..." << endl;
    }
    MyException(const MyException &other) : message_(other.message_)
    {
        cout << "Copy MyException ..." << endl;
    }
    virtual ~MyException()
    {
        cout << "~MyException ..." << endl;
    }

    const char *what() const
    {
        return message_.c_str();
    }
private:
    string message_;
};

class MyExceptionD : public MyException
{
public:
    MyExceptionD(const char *message)
        : MyException(message)
    {
        cout << "MyExceptionD ..." << endl;
    }
    MyExceptionD(const MyExceptionD &other)
        : MyException(other)
    {
        cout << "Copy MyExceptionD ..." << endl;
    }
    ~MyExceptionD()
    {
        cout << "~MyExceptionD ..." << endl;
    }
};

int main(void)
{
    try
    {
        MyExceptionD e("test exception");
        throw e;
    }
    catch (MyExceptionD &e)
    {
        cout << "catch MyExceptionD ..." << endl;
        cout << e.what() << endl;
    }
    catch (MyException &e)
    {
        cout << "catch MyException ..." << endl;
        cout << e.what() << endl;
    }

    return 0;
}


Replace the capture order of base and inherited classes

catch (MyException &e)
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	catch (MyExceptionD &e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e.what() << endl;
	}


Picture explanation:
If the exception of the derived class can be caught by the base class, and the previous exception handler can match, then catch first.
1. MyExceptionD e("test exception"); based on the order of constructor calls, the base class is called first, then the derived class
2. throw e; first call the base class, then the derived class, and then the destructor, because it's a temporary object, according to the order in which the copy constructor is called. In reverse order to constructors, derive first, and then base class.
3. The * * catch (myexception & E * * base class catches the exception, and then parses the object created by the constructor at the beginning, which is in the reverse order of the constructor. It derives first and then the base class.

be careful:
If you put the base class first and not in the form of a reference, such as catch (MyException e), then it will be caught by this place, and there will be the problem of object slicing in the process of constructing e. For example:

catch (MyException e)//Not in the form of a reference
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	catch (MyExceptionD &e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e.what() << endl;
	}

7. Exceptions and pointers

Throwing a pointer is usually a bad idea, because throwing a pointer requires that the object pointed to by the pointer exists anywhere in the corresponding processing code (note that the pointer itself is copied when throwing at this time, and the content pointed to by the pointer will not be copied)

#include <iostream>
#include <string>

using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	virtual ~MyException()
	{
		cout << "~MyException ..." << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

class MyExceptionD : public MyException
{
public:
	MyExceptionD(const char *message)
		: MyException(message)
	{
		cout << "MyExceptionD ..." << endl;
	}
	MyExceptionD(const MyExceptionD &other)
		: MyException(other)
	{
		cout << "Copy MyExceptionD ..." << endl;
	}
	~MyExceptionD()
	{
		cout << "~MyExceptionD ..." << endl;
	}
};

int main(void)
{
	try
	{
		throw new MyExceptionD("test exception");
	}
	catch (MyExceptionD *e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e->what() << endl;
		delete e;
	}
	catch (MyException &e)
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	return 0;
}


The myexception and myexceptiond classes are as shown above. Now the myexceptiond object is allocated on the heap, so it has not been released at the time of catch, and you can also access e - > what(); but you need to delete e at the end of catch;

It is assumed that
throw new MyExceptionD("test exception");
change into
MyExceptionD e("test exception");
throw &e;

int main(void)
{
	try
	{
		MyExceptionD e("test exception");
		throw &e;
	}
	catch (MyExceptionD *e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e->what() << endl;
		//delete e;
	}
	catch (MyException &e)
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	return 0;
}


Program description: no test exception output
That is, a pointer to a local object is thrown. Since MyExeptionD object has been destructed during catch, e - > what(); that is, e is a null suspension pointer.

Another point is that any type of pointer can be caught by a void * pointer. If you open the comment, the exception will be caught by it first because it is listed first.

int main(void)
{
	try
	{
		throw new MyExceptionD("test exception");
	}
	catch (void* e)
	{
	cout<<"catch void* ..."<<endl;
	cout<<((MyExceptionD*)e)->what()<<endl;
	delete (MyExceptionD*)e;
	}
	catch (MyExceptionD *e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e->what() << endl;
		delete e;
	}
	catch (MyException &e)
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}
	return 0;
}

8. Abnormal specification

1

  1. The purpose of the exception specification is to let the function user know what exceptions the function may throw.
    You can list all the exception types that this function might throw in its declaration.
    For example:

void fun() throw(A,B,C,D);

  1. If there is no exception interface declaration, this function can throw any type of exception.

3,

  1. A function that does not throw any type of exception is declared as follows:

void fun() throw();

#include <iostream>
#include <string>

using namespace std;

class MyException
{
public:
	MyException(const char *message)
		: message_(message)
	{
		cout << "MyException ..." << endl;
	}
	MyException(const MyException &other) : message_(other.message_)
	{
		cout << "Copy MyException ..." << endl;
	}
	virtual ~MyException()
	{
		cout << "~MyException ..." << endl;
	}

	const char *what() const
	{
		return message_.c_str();
	}
private:
	string message_;
};

class MyExceptionD : public MyException
{
public:
	MyExceptionD(const char *message)
		: MyException(message)
	{
		cout << "MyExceptionD ..." << endl;
	}
	MyExceptionD(const MyExceptionD &other)
		: MyException(other)
	{
		cout << "Copy MyExceptionD ..." << endl;
	}
	~MyExceptionD()
	{
		cout << "~MyExceptionD ..." << endl;
	}
};

void fun(int n) throw (int, MyException, MyExceptionD)
{
	if (n == 1)
	{
		throw 1;
	}
	else if (n == 2)
	{
		throw MyException("test Exception");
	}
	else if (n == 3)
	{
		throw MyExceptionD("test ExceptionD");
	}

}

void fun2() throw()
{

}

int main(void)
{
	try
	{
		fun(2);
	}

	catch (int n)
	{
		cout << "catch int ..." << endl;
		cout << "n=" << n << endl;
	}
	catch (MyExceptionD &e)
	{
		cout << "catch MyExceptionD ..." << endl;
		cout << e.what() << endl;
	}
	catch (MyException &e)
	{
		cout << "catch MyException ..." << endl;
		cout << e.what() << endl;
	}

	return 0;
}


In fact, compilation produces warnings:

warning C4290: ignoring C + + exception specification, but indicating function is not__ declspec(nothrow)

That is to say, VC + + compiler does not support exception specification
For example,
void fun(int n) throw (int, MyException, MyExceptionD);
No exception of type double is declared, but throw 1.0 within the function; external catch (double) will succeed.

Tags: Linux C

Posted on Fri, 05 Jun 2020 05:11:20 -0400 by Swerve1000