Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the anomalies in C++

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly explains "how to understand the anomalies in C++". The content in the article is simple and clear, and it is easy to learn and understand. Now please follow the editor's train of thought slowly and deeply. Let's study and learn how to understand the anomalies in C++.

What is exception handling

In a word: exception handling is an error in the handler.

Second, why do you need exception handling, the basic idea of exception handling

Bjarne Stroustrup, the father of C++, said in "The C++ Programming Language" that a library's author can detect runtime errors, but generally doesn't know how to handle them (because of the user's specific application); on the other hand, library users know how to handle these errors, but can't check when they occur (if they can detect them, they can handle them in the user's code, without leaving it to the library to find them).

Bjarne Stroustrup says that the basic purpose of providing exceptions is to deal with the above problems. The basic idea is to have a function throw an exception when it finds an error it cannot handle, and then its (direct or indirect) caller can handle the problem.

The fundamental idea is that a function that finds a problem it cannot cope with throws an exception, hoping that its (direct or indirect) caller can handle the problem.

That is, what C++ primer says: separate problem detection from problem handling.

Exceptions let us separate problem detection from problem resolution

One idea: in all programming languages that support exception handling (such as java), it is important to realize that in the process of exception handling, the problem detection code can throw an object to the problem handling code, and through the type and content of this object, the communication between the two parts is actually completed, and the content of the communication is "what's wrong". Of course, various languages have more or less differences in the specific implementation of exceptions, but the idea of this communication is the same.

Third, the way to handle errors before the occurrence of exceptions

In the world of C, errors are always handled in two ways: one is to use the return value of an integer to identify the error, and the other is to use the errno macro (which can be simply understood as a global integer variable) to log the error. Of course, these two methods can still be used in C++.

The biggest drawback of these two methods is that there will be inconsistencies. For example, some functions return 1 for success and 0 for error, while others return 0 for success and non-0 for error.

Another disadvantage is that there is only one return value of the function. If you represent the error code by the return value of the function, then the function cannot return other values. Of course, you can return another value through a pointer or a reference to C++, but this may make your program slightly obscure.

Fourth, why is abnormal good?

The advantages of using exception handling are as follows:

1. The return value of the function can be ignored, but the exception cannot be ignored. If the program has an exception but is not caught, the program will be terminated, which will more or less make the program developed by the programmer a little more robust. If you use the error macro or function return value of C language, the caller may forget to check and do not deal with the error, resulting in the inexplicable termination of the program or the wrong result.

two。 The integer return value does not have any semantic information. Exceptions, on the other hand, contain semantic information, which is sometimes reflected in the class name.

3. The integer return value lacks relevant context information. An exception, as a class, can have its own members, which can convey enough information.

4. Exception handling can be skipped in the call. This is a coding problem: suppose there is an error in the call stack with multiple functions, using integer return codes requires you to handle them at each level of the function. Using the stack expansion mechanism of exception handling, it only needs to be handled in one place, and does not need to be handled by every level of function.

V. problems that should be paid attention to when using exceptions in C++

Everything has two sides, and exception has both advantages and disadvantages. If you are a C++ programmer and want to use exceptions in your code, here are some questions you should pay attention to.

1. Performance issues. This is not usually a bottleneck, but if you are writing software with high performance or strong real-time requirements, you need to consider it.

If you used to be a java programmer, like me, the following things may confuse you for a while, but you can't help it. You're learning C++. )

two。 Memory recovery problems caused by pointers and dynamic allocation: in C++, dynamically allocated memory is not automatically recycled. If you encounter an exception, you need to consider whether the memory is reclaimed correctly. In java, you basically don't need to think about this. It's nice to have a garbage collection mechanism!

3. Function exception throw list: in java, if a function does not explicitly specify the exception to be thrown in the exception throw list, it is not allowed to be thrown; but in C++, if you do not specify the exception to be thrown in the function's exception throw list, it means you can throw any exception.

4. The exception thrown list of functions is not checked at compile time in C++. This means that when you write a C++ program, if you throw an exception in the function that is not declared in the exception throw list, there will be no error at compile time. And in java, the prompt function of eclipse is really powerful!

5. In java, the exception thrown has to be an exception class; but in C++, you can throw any type, and you can even throw an integer. (of course, if you receive in C++ using an object instead of a reference in catch, then the object you throw must be replicable. This is a requirement of language, not exception handling.

6. There is no finally keyword in C++. Both java and python have the keyword finally.

6. The basic grammar of exception 1. Throw and catch exceptions

Very simple, throw an exception with throw, capture with try. Catch .

Considerations when catching exceptions:

1. The exception specifier in the catch clause must be fully typed and cannot be pre-declared, because you often have to access the members of the exception class in your exception handling. Exception: only if your catch clause uses pointers or references to receive parameters, and you do not access members of the exception class within the catch clause, then the exception descriptor of your catch clause can be a pre-declared type.

2. The matching process of catch is to find the first match, not the best match.

3. In the matching process of catch, the requirements for types are relatively strict. Standard arithmetic conversions and class type conversions are not allowed. Class type conversions include two types: implicit type conversions through constructors and type conversions through conversion operators.

4. The same as the function parameters are:

① if the base class object is used in catch to receive the subclass object, it will cause the subclass object to be separated (slice) as the parent subobject (by calling the copy constructor of the parent class)

② if the subclass object is accepted in catch using a reference to the base class object, dynamic binding, that is, polymorphic invocation, occurs when the virtual member is accessed.

③ if you use a pointer to a base class object in catch, make sure that the throw statement also throws a pointer type, and that the object the pointer points to still exists during the execution of the catch statement (usually a dynamically allocated object pointer).

5. Different from function parameters are:

① if an object is thrown in throw, no matter what reception is used in catch (base class object, reference, pointer, or subclass object, reference, pointer), the compiler will construct another copy of the object before passing it to catch. That is, if you throw an object type in a throw statement and receive it through and through an object at catch, then the object goes through two copies, that is, the copy constructor is called twice. Once in the throw, the "thrown to the object" will be copied to a "temporary object" (this step is necessary), and then because catch uses the object to receive, then you need to copy from the "temporary object" to the "formal parameter variable of catch"; if you use "reference" to receive parameters in catch, you do not need a second copy, that is, the reference of the formal parameter points to the temporary variable.

② the type of this object is the same as the static type embodied in the throw statement. In other words, if you throw a parent class reference to a subclass object in a throw statement, a split occurs, that is, only the parent part of the subclass object will be thrown, and the type of the thrown object is also the parent type. (in terms of implementation, this is because when copying to a "temporary object", the copy constructor of the type (in this case, the parent class) in the throw statement is used.

③ cannot perform standard arithmetic conversions and custom conversions of classes: a lot of type conversions can be done in the process of function parameter matching. However, in the process of exception matching, the rules of conversion should be strict.

The matching process of ④ exception handling mechanism is to find the first match (first fit), and the process of function call is to find the best match (best fit).

two。 Exception type

As mentioned above, in C++, you can throw any type of exception. (alas, you can throw any type. When I first saw this, I didn't react for a long time, because it doesn't work in java.)

Note: as mentioned above, in C++, if you throw an object in the throw statement, the object you throw must be replicable. Because replica delivery is required, this is a requirement of the language, not exception handling. (it is also mentioned in the above "differences from function parameters", because it is copied to a temporary variable first.)

3. Stack expansion

Stack expansion refers to the process of matching catch when an exception is thrown.

When an exception is thrown, the execution of the current function is paused and a matching catch clause is started. Look up the chain of nested calls to the function until a matching catch clause is found, or no matching catch clause is found.

Note:

1. During stack expansion, local objects are destroyed.

① if the local object is a class object, it is destroyed by calling its destructor.

② but for objects that are dynamically allocated, the compiler does not delete them automatically, so we have to delete them explicitly manually. (this problem is so common and important that a method called RAII is used, as described below.)

two。 Destructors should never throw exceptions. If you need to execute code in the destructor that may throw an exception, you should handle the exception inside the destructor instead of throwing it.

Reason: when a stack is unfolded for an exception, the destructor throws its own unhandled exception, which will result in a call to the standard library terminate function. The default terminate function calls the abort function, forcing an abnormal exit from the entire program.

3. An exception can be thrown in the constructor. Note, however, that if the constructor exits because of an exception, the destructor of this class cannot be executed. So manually destroy the parts that were constructed before the exception was thrown.

4. Exception re-thrown

Syntax: use an empty throw statement. It is written as: throw

Pay attention to the problems:

The location where the ① throw; statement can appear can only be in the catch clause or in a function called by the catch clause.

② re-throws the original exception object, the "temporary variable" mentioned above, not the catch parameter.

③ should use reference parameters in catch if it wants to modify the exception object before it is rethrown. If you use an object to receive, after modifying the exception object, you cannot propagate the modified exception object by "re-throwing", because the re-throw is not a catch formal parameter, but should use throw e; here "e" is the object parameter received in the catch statement.

5. Catch all exceptions (match any exceptions)

Syntax: in the catch statement, use three dots (… ). It is written as: catch (…) The three dots here are "wildcards", similar to variable-length formal parameters.

Common usage: use with the "re-throw" expression, do some of the work in catch, and then rethrow the exception.

6. Uncaught exception

This means that if an exception is thrown in the program, it must be caught. Otherwise, if an exception is thrown during the execution of the program, and the corresponding catch statement is not found, then the terminate function will be called like "the destructor throws an exception during stack expansion", and the default terminate function will call the abort function to force an abnormal exit from the whole program.

7. Function test block of constructor

Exceptions thrown in the initialization list of the constructor must be caught using a function test block (function try block). The following forms of syntax types:

MyClass::MyClass (int I) try: member (I) {/ / function body} catch (exception parameter) {/ / exception handling code}

Note: for exceptions caught in the function test block, a memory release operation can be performed in the catch statement, and then the exception will still be thrown into the user code again.

8. Exception throw list (exception description exception specification)

It is after the formal parameter table of the function (or after const in the case of a const member function) that the keyword throw is used to declare a parenthesized and possibly empty list of exception types. Such as: throw () or throw (runtime_error, bad_alloc).

Meaning: indicates that the function can only throw exception types in the list. For example: throw () means no exception is thrown. Throw (runtime_error, bad_alloc) indicates that only two exceptions can be thrown: runtime_error or bad_alloc.

Note: (especially for those who studied java before, it is not quite the same as in java)

① if the function does not explicitly declare that it throws a list, it means that the exception can throw an arbitrary list. In java, if there is no exception thrown list, then no exception can be thrown.

The "throw ()" of ② C++ is equivalent to java's undeclared throw list. Indicates that no exception is thrown.

③ in C++, the compiler does not check the list of exceptions thrown at compile time. That is, if you declare a unexpeced exception list, even if your function code throws an exception that is not specified in the throw list, your program can still be compiled and won't make an error until runtime. For such an exception, it is called "unexpected exception" in C++. This is not the same as java, where java requires strict inspection.

Handling of unexpected exceptions:

If an unexpected exception occurs in the program, the program calls the function unexpected (). The default implementation of this function is to call the terminate function, which eventually terminates the program by default.

Restrictions on exception throwing lists when virtual function overloading methods:

When overloaded in a subclass, the exception description of the function must be as strict or more stringent than in the parent class. In other words, the exception of the corresponding function in the subclass indicates that no new exception can be added. Or in other words: the exception-thrown list in the parent class is a subclass overloaded version of the virtual function that can throw a superset of the exception list.

Restrictions on exception throwing lists in function pointers:

The exception throw list is part of the function type, and you can also specify the exception throw list in the function pointer. However, when initializing or assigning a function pointer, in addition to checking the return value and formal parameters, you should also pay attention to the limitations of the exception throwing list: the exception description of the source pointer must be at least as strict as that of the target pointer. Compare a mouthful, in other words, that is, the exception specified when declaring a function pointer throws a list. Be sure that the exception of the actual function throws a superset of the list. If you do not provide an exception throw list when defining a function pointer, you can point to a function that can throw any type of exception.

Whether it is useful to throw a list:

In Article 14 of More effective C++, Scott Meyers points out that "exception descriptions" (Use exception specifications judiciously) should be used carefully. "exception descriptions" are all of our "exception throwing lists". The fundamental reason for caution is that the C++ compiler will not check for the exception throw list, which may throw an exception in the function code or in the called function that is not specified in the throw list, resulting in the program calling the unexpected function and causing the program to terminate prematurely. At the same time, he gave three things to consider:

① do not use exceptions to throw lists in templates. The reason is simple: if you don't even know the type used for the instance template, you can't determine whether or not the function should throw an exception.

② if the B function is called within the A function and the B function does not declare the exception throw list, then the A function itself should not set the exception throw list. (the reason is that the B function may throw an exception that is not declared in the exception throw list of the A function, resulting in a call to the unex function.)

③ specifies a new unexpected function through the set_unexpected function, catches an exception in that function, and throws a uniform type of exception.

In addition, it is pointed out in the "C++ Primer" 4th that although the application of exceptions is limited, it is good to explicitly declare that the function does not throw any exceptions if it is certain that the function will not throw any exceptions. Through the statement: "throw ()". The advantage is that programmers don't have to worry about exceptions when calling such a function. For the compiler, you can perform optimizations that are suppressed by possible exceptions.

7. Exception classes in the standard library

Like java, many exception classes are provided in the standard library, which are organized by class inheritance. Standard anomalies are organized into eight.

The inheritance hierarchy of exception classes is as follows:

The header file where each class is located is identified at the bottom of the figure.

Members of the standard exception class:

① in the above inheritance system, each class provides constructors, copy constructors, and assignment operator overloads.

② logic_error class and its subclasses, runtime_error class and its subclasses, their constructors accept a formal parameter of type string, which is used to describe exception information.

All ③ exception classes have a what () method that returns a value of type const char* (C-style string) that describes the exception information.

Specific description of the standard exception class:

Exception name describes the parent class of all standard exception classes in exception: bad_alloc when operator new and operator new [], when the request for memory allocation fails, bad_exception this is a special exception. If the bad_exception exception is declared in the function's exception throw list, when the function throws an exception that is not in the list, this is the exception that is thrown in the called unexpected function, no matter what type Will be replaced with the bad_exception type bad_typeid uses the typeid operator to manipulate a NULL pointer, and the pointer is a class with a virtual function, and then a bad_typeid exception is thrown. When bad_cast fails to use dynamic_cast conversion references, there is an error logic_error logic error in the ios_base::failureio operation process, errors that can be detected before run runtime_error runtime errors, errors that can only be detected at run time

Subclasses of logic_error:

Returns the pointer saved by ap ap.reset (p) if the value of p is different from that of ap, delete the object pointed to by ap and bind ap to pap.release () return the pointer saved by ap and make ap unbound ap.get () return the pointer saved by ap

Use of the auto_ptr class:

1. Used to hold a pointer to the object type. Note that it must be a pointer to dynamically allocated objects (that is, unallocated using new). It cannot be either a dynamically allocated array (using new []) pointer or a non-dynamically allocated object pointer.

two。 The usual initialization method: in user code, use new expressions as arguments to the auto_ptr constructor. (note: the auto_ptr class accepts the constructor of the pointer parameter as explicit, so it must be initialized explicitly.)

3. The behavioral characteristics of auto_ptr: similar to ordinary pointer behavior. The main reason auto_ptr exists is that in order to prevent memory leaks caused by dynamically allocated object pointers, since they are pointers, they have "*" and "- >" operators. So the main purpose of auto_ptr is to first ensure that objects referenced by auto_ptr are automatically deleted and support normal pointer behavior.

4. The replication and assignment of auto_ptr objects is destructive. ① will cause the right Operand to become unbound, so that the auto_ptr object cannot be placed in the container. When ② assigns a value, it modifies the operator to be unbound, that is, it modifies the right Operand, so make sure that the right Operand of the assignment operator here is a modifiable left value (however, in a normal assignment operator, the right Operand may not be a left value) ③, like a normal assignment operator, has no effect if it is self-assigning; ④ causes auto_ptr objects not to be placed in the container.

5. If auto_ptr is initialized using the default constructor to become an unbound auto_ptr object, it can be bound to an object through the reset operation.

6. If you want to test whether auto_ptr is already bound to an object, use the return value of the get () function to compare with NULL.

Defects in auto_ptr:

1. You cannot use auto_ptr objects to hold pointers to statically allocated objects, nor can you save pointers to dynamically allocated arrays.

two。 You cannot say that two auto_ptr objects point to the same object. Because after one auto_ptr object is destructed, another auto_ptr object points to the memory that has been freed. The two main common reasons for this are: ① initializes or reset two different auto_ptr objects with the same pointer, and ② uses the get function return value of one auto_ptr object to initialize or reset another auto_ptr object.

3. You cannot put an auto_ptr object in a container. Because its copy and assignment operations are destructive.

Common exception handling problems

Dynamic memory allocation error

① allocates dynamic memory using the new and new [] operators, and if they fail to allocate memory, they throw bad_alloc exceptions in the new header file, so we should catch these exceptions in our code. Common code forms are as follows:

Try {/ / other code ptr = newint [num _ max]; / other code} catch (bad_alloc & e) {/ / the common way of handling here is to release the allocated memory first, then end the program, or print an error message and continue to execute}

② can be handled in a way similar to the C language, but the version of nothrow to be used at this time allocates memory in the form of "new (nothrow)". At this point, if the allocation is unsuccessful, the NULL pointer is returned instead of throwing a bad_alloc exception.

③ can customize the memory allocation failure behavior. C++ allows you to specify a new handler (newhandler) callback function. There is no new handler by default, and if we set the new handler, when new and new [] fail to allocate memory, the new handler we set will be called instead of throwing an exception directly. The callback function is set through the set_new_handler function. The function that requires the callback has no return value and no formal arguments.

Advice from Bjarne Stroustrup, the father of C++

Excerpt from "The C++ Programming Language"-Bjarne Stroustrup, the father of C++

1.Don't use exceptions where more local control structures will suffice; do not use exceptions when local control can handle

2.Use the "resource allocation is initialization" technique to manage resources; uses "resource allocation is initialization" technology to manage resources

3.Minimize the use of try-blocks. Use "resource acquisition is initialization" instead of explicit handler code; uses as few try-catch statement blocks as possible, instead using the "resource allocation is initialization" technique.

4.Throw an exception to indicate failure in a constructor; if an error occurs in the constructor, it is indicated by throwing an exception.

5.Avoid throwing exceptions from destructors; avoids throwing exceptions in destructors.

6.Keep ordinary code and error-handling code separate; keeps normal program code and exception handling code separate.

7.Beware of memory leaks caused by memory allocated bynewnot being released in case of an exception; is careful that if an exception occurs in memory allocated through new, it may cause a memory leak.

8.Assume that every exception that can be thrown by a function will be thrown; if a function may throw an exception, then when we call it, we have to assume that it will throw the exception, that is, to handle it.

9.Don't assume that every exception is derived from classexception; keep in mind that not all exceptions inherit from the exception class.

10.A library shouldn't unilaterally terminate a program. Instead, a library written by throw an exception and let a caller decide; for others to call, should not end the program, but should let the caller decide what to do by throwing an exception (because the caller must handle the thrown exception).

If 11.Develop an error-handling strategy early in a design; develops a project, it is necessary to determine an "error handling strategy" during the design phase.

Thank you for your reading. The above is the content of "how to understand the anomalies in C++". After the study of this article, I believe you have a deeper understanding of how to understand the anomalies in C++. The specific use also needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report