"Pure virtual function call" in C ++"

Source: Internet
Author: User

A few days ago, our project just solved the stopship bug caused by pure virtual function call. Let's take a look at the hot tie.

Theoretical case

When a pure virtual function is called, the following dialog box is displayed in the debug mode of vc ++:

Then there is crash.

I found this on the internet and found someone has made a detailed introduction to it: "Pure Virtual Function Called": An Explanation. this is a quite comprehensive article. Starting from the abstract base class of pure virtual functions, this article introduces the concepts of vptr and vtable in the object model and their construction and analysis processes. With these foundations, the author then lists the possible "pure virtual function call" situations in section 5, which can be summarized into two types:

  • Call pure virtual functions directly or indirectly in the constructors or destructor of the base class.
    For example, indirect call of pure virtual functions in the base class constructor:
    Class Base {public: Base () {callVirtual ();} void callVirtual () {virtualFunc ();} virtual void virtualFunc () = 0 ;}; class Derived: public Base {public: virtual void virtualFunc () {}}; Derived d; // calls pure virtual functions during the construction process
  • Use a wild pointer to call a virtual function
    In the above example, but not calling callVirtual in the base class constructor:
     Derived* pD = new Derived;     Base* pB = pD;     delete pD;     pB->virtualFunc();

In fact, for the first case, if you directly call pure virtual functions in the base class constructor or destructor, the compiler should be able to catch this error; although the compiler cannot detect indirect calls, Scott promotes Item 9: Never call virtual functions during construction or destruction in <negative tive C ++>, in this case, the probability of occurrence should be relatively small. In addition, even if the occurrence occurs, it is relatively simple to troubleshoot the error.

In the second case, although the behavior of the wild pointer is undefined, as far as I know, we generally get an "access violation" instead of "pure virutal function call ":

 

Case in reality

The situations we encounter in reality will be more complex than the above mentioned: a subclass object encounters an exception in the structure process and is not completely destroyed, leaving the next "defective" object, the program continues to use this defective object to call pure virtual functions:

class Base{public:~Base(){throw 0;} // . . . a)virtual void virtualFunc() = 0;};class Derived: public Base{public:virtual void virtualFunc(){}};Base* pB = new Derived;__try{delete pB; // . . . b)pB = NULL;}__except(EXCEPTION_EXECUTE_HANDLER){pB->virtualFunc(); // . . . c)}

When B) parses the Derived object, an exception is thrown at a in its basic class destructor. At this time, because the Derived destructor has been called, the vptr in this object has already pointed to the vtable of the base class, thus forming a "defective" object that cannot be constructed according to the normal process. When you use this object in c) when virtualFunc is called, the "pure virtual function call" error is naturally caused.

Note that when you encounter "pure virtual function call", it may be far away from the real error point, that is, the exception point thrown in the destructor. Therefore, this situation is relatively difficult to debug.

Destructor and exceptions

Okay, I know you 've been holding on for a long time, and you wanted to say, "You shouldn't have thrown an exception in the Destructor! ", As Scott said: Item 11: Prevent exceptions from leaving destructors; as mentioned in the C ++ FAQ: Never throw an exception from a destructor.

Although some people come up saying that there is nothing wrong with throwing destructors, I still support your opinion. We should not throw an exception in the destructor. Otherwise, we have to deal with the following two serious problems:

  • The program exits due to a secondary exception;
  • Subsequent problems caused by incomplete destroyed objects and incomplete work

    This is the case for pure virtual function call.

But there is always a gap between ideal and reality. You have to deal with some things:

  1. For more than 10 years, millions of lines of code, countless people have maintained the code base, no one can guarantee whether a destructor will directly or indirectly throw an exception.
  2. Maybe we should take responsibility for 1st cases, that is, our own code, and perform a comprehensive check on the original code, and ensure that the subsequent code will not throw an exception in the destructor. But even so, if we call a third-party library function in the destructor, what if this function throws an exception?
  3. Even if the called functions (including their own and third-party functions) do not explicitly throw an exception, when we use SEH to handle the exception, if there is a division of 0 operation in the code, access violation and so on will still be captured as exceptions.
  4. What if an exception is added to each non-trivial destructor? This code is too uugly. Besides, this code is just in case and does not make a lot of sense if you do not take the initiative to throw an exception.

Therefore, it is quite difficult to avoid exceptions in the destructor. Herb Sutter once proposed a proposal for the C ++ language: to make the Destructor unable to throw an exception and solve it in a language level, but it was rejected by Bjarne Stroustrup, Andy Koenig, and others. This will lead to inconsistent behavior of the original program. In rare cases, we still hope to throw exceptions.

My conclusion here is: no initiative, no rejection. Inactive means to prevent exceptions from being thrown in the destructor in principle. If you do not reject exceptions, you do not need to force swallow exceptions in the destructor, instead, the possible problems are exposed to facilitate the solution. For example, in the real-world example mentioned above, by throwing an exception and then calling pure virtual function call to expose the problem, we can start to study why an exception is thrown, how to handle this problem and prevent it from throwing an exception ~~~

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.