zoukankan      html  css  js  c++  java
  • 深入解析pure virtual function call

    在本文中,我们将不解释为什么会提示“纯虚拟函数调用”和如何提示“纯虚拟函数调用”,而是详细解释在win32平台的构造函数/析构函数中直接/间接调用纯虚拟函数时程序本身。在开始时,将显示一个经典示例,在这个示例中,它将提示一个带有“纯虚拟函数调用”的消息框。

        /** 
         * "pure virtual function call" on win32 platform 
         * filename: testWin32PVFC.cpp 
         */  
        #include <iostream>  
         
        #define PI 3.1415926  
        using namespace std;  
          
        class Shape  
        {  
        private:  
            double ValuePerSquareUnit;  
          
        protected:  
            Shape(double valuePerSquareUnit):  
                ValuePerSquareUnit(valuePerSquareUnit)  
            {  
                //error LNK2001: unresolved external symbol "public: virtual double __thiscall Shape::area(void)const " (?area@Shape@@UBENXZ)  
                //std::cout << "creating shape, area = " << area() << std::endl;  
                std::cout << "creating shape, value = " << value() << std::endl;  //indirectly call pure virtual function in constructor  
            }  
          
        public:  
            virtual double area() const = 0;  
          
            double value() const  
            {  
                return ValuePerSquareUnit * area();  
            }  
          
            virtual ~Shape()  
            {  
                printf("Shape::~Shape() is called");  
            }  
          
            double getPerSquareUnit()  
            {  
                return ValuePerSquareUnit;  
            }  
        };  
          
        class Rectangle : public Shape  
        {  
        private:  
            double Width;  
            double Height;  
          
        public:  
            Rectangle(double width, double height, double valuePerSquareUnit):  
                Shape(valuePerSquareUnit),Width(width),Height(height)  
            {  
            }  
          
            virtual ~Rectangle()  //can be removed  
            {  
            }  
          
            virtual double area() const  
            {  
                return Width * Height;  
            }  
          
        };  
          
        class Circle: public Shape  
        {  
            double Radius;  
          
        public:  
            Circle(double radius, double valuePerSquareUnit):  
                Shape(valuePerSquareUnit),Radius(radius)  
            {  
            }  
          
            virtual ~Circle()  //can be removed  
            {  
            }  
          
            virtual double area() const  
            {  
                return PI * Radius * Radius;  
            }  
        };  
          
          
        int main()  
        {  
            Rectangle* pr = new Rectangle(30, 20, 10);  
            Circle* pc = new Circle(15, 10);  
          
            //invoke Rectangle::area()  
            printf("rectangle: area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", pr->area(), pr->getPerSquareUnit(), pr->value());  
            //invoke Circle::area()  
            printf("circle   : area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", pc->area(), pc->getPerSquareUnit(), pc->value());  
          
            Shape* shape;  
            shape = pr;  
            printf("rectangle: area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", shape->area(), shape->getPerSquareUnit(), shape->value());  
          
            shape = pc;  
            printf("circle   : area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", shape->area(), shape->getPerSquareUnit(), shape->value());  
          
            return 0;  
        }  
    

     编译执行上面的代码,报

    从这个例子我们可以得出结论, 在构造函数/析构函数中,直接调用纯虚函数,会出现编译错误,如
    error LNK2001: unresolved external symbol "public: virtual double __thiscall Shape::area(void)const " (?area@Shape@@UBENXZ)
    间接调用纯虚函数,提示“pure virtual function call”
    调试这个程序,我们可以看到下面列出的Shape::value()的反汇编代码,我的注释被嵌入了。
     double value() const
    
        {
    
    004118F0  push        ebp 
    
    004118F1  mov         ebp,esp
    
    004118F3  sub         esp,0CCh
    
    004118F9  push        ebx 
    
    004118FA  push        esi 
    
    004118FB  push        edi 
    
    004118FC  push        ecx 
    
    004118FD  lea         edi,[ebp-0CCh]
    
    00411903  mov         ecx,33h
    
    00411908  mov         eax,0CCCCCCCCh
    
    0041190D  rep stos    dword ptr es:[edi]
    
    0041190F  pop         ecx 
    
    00411910  mov         dword ptr [ebp-8],ecx
    
        return ValuePerSquareUnit * area();
    
    00411913  mov         eax,dword ptr [this]     //eax = 0x003b5fc0, move ‘this’ pointer to eax
    
    00411916  mov         edx,dword ptr [eax]      //edx = 0x00417800, move vfptr to edx
    
    00411918  mov         esi,esp
    
    0041191A  mov         ecx,dword ptr [this]    //ecx = 0x003b5fc0, move ‘this’ pointer to ecx
    
    0041191D  mov         eax,dword ptr [edx]     //eax = 0x004111f4, the address of __purecall, move the first virtual function address to eax
    
    0041191F  call        eax                     //call this virtual function
    
    00411921  cmp         esi,esp
    
    00411923  call        @ILT+500(__RTC_CheckEsp) (4111F9h)
    
    00411928  mov         ecx,dword ptr [this]
    
    0041192B  fmul        qword ptr [ecx+8]
    
        }
    
    0041192E  pop         edi 
    
    0041192F  pop         esi 
    
    00411930  pop         ebx 
    
    00411931  add         esp,0CCh
    
    00411937  cmp         ebp,esp
    
    00411939  call        @ILT+500(__RTC_CheckEsp) (4111F9h)
    
    0041193E  mov         esp,ebp
    
    00411940  pop         ebp 
    
    00411941  ret    

    可以从下图中验证反汇编代码和我的注释,下图是从调试中捕获的。

     

    要找到0x004111f4的地址,需要在反汇编代码中找到该程序的跳转表。然后,我们发现它列在下面,其中列出了所有跳转项。

    00411005  jmp         _setdefaultprecision (413E80h)

    0041100A  jmp         _setargv (413F20h)

    0041100F  jmp         std::ios_base::good (41283Ah)

    00411014  jmp         DebugBreak (414B78h)

    00411019  jmp         _RTC_GetErrDesc (413BE0h)

    0041101E  jmp         Rectangle::area (411BD0h)

    00411023  jmp         __p__fmode (413F94h)

    00411028  jmp         __security_check_cookie (412870h)

    0041102D  jmp         IsDebuggerPresent (414B6Ch)

    00411032  jmp         std::basic_ostream<char,std::char_traits<char> >::sentry::operator bool (412630h)

    00411037  jmp         type_info::operator= (412BA0h)

    0041103C  jmp         _RTC_Terminate (413F60h)

    00411041  jmp         WideCharToMultiByte (414B7Eh)

    00411046  jmp         _RTC_AllocaHelper (412940h)

    0041104B  jmp         _RTC_GetErrorFuncW (413CA0h)

    00411050  jmp         _RTC_NumErrors (413BD0h)

    00411055  jmp         std::basic_ios<char,std::char_traits<char> >::rdbuf (412810h)

    0041105A  jmp         __setusermatherr (413F04h)

    0041105F  jmp         Sleep (414B48h)

    00411064  jmp         type_info::_type_info_dtor_internal_method (414B12h)

    00411069  jmp         Circle::`scalar deleting destructor' (411DC0h)

    0041106E  jmp         Rectangle::Rectangle (4119A0h)

    00411073  jmp         std::basic_ios<char,std::char_traits<char> >::setstate (4127ECh)

    00411078  jmp         GetModuleFileNameW (414BD2h)

    0041107D  jmp         __security_init_cookie (414120h)

    00411082  jmp         Shape::getPerSquareUnit (411960h)

    00411087  jmp         Circle::`scalar deleting destructor' (411DC0h)

    0041108C  jmp         SetUnhandledExceptionFilter (414B66h)

    00411091  jmp         _cexit (41428Ch)

    00411096  jmp         Shape::`scalar deleting destructor' (411AF0h)

    0041109B  jmp         _CrtDbgReportW (414504h)

    004110A0  jmp         VirtualQuery (414BD8h)

    004110A5  jmp         atexit (4140E0h)

    004110AA  jmp         MultiByteToWideChar (414B84h)

    004110AF  jmp         FatalAppExitA (414BBAh)

    004110B4  jmp         std::endl (4127E6h)

    004110B9  jmp         _RTC_SetErrorType (413C00h)

    004110BE  jmp         _except_handler4 (414520h)

    004110C3  jmp         _lock (414B30h)

    004110C8  jmp         std::basic_streambuf<char,std::char_traits<char> >::_Unlock (412852h)

    004110CD  jmp         GetProcAddress (414B90h)

    004110D2  jmp         std::char_traits<char>::length (412828h)

    004110D7  jmp         _RTC_CheckStackVars (4128C0h)

    004110DC  jmp         operator delete (412858h)

    004110E1  jmp         std::char_traits<char>::eq_int_type (4127FEh)

    004110E6  jmp         type_info::_type_info_dtor_internal_method (414B12h)

    004110EB  jmp         std::uncaught_exception (412846h)

    004110F0  jmp         __report_gsfailure (4130E0h)

    004110F5  jmp         terminate (414B0Ch)

    004110FA  jmp         _exit (414280h)

    004110FF  jmp         GetCurrentThreadId (414BA8h)

    00411104  jmp         _initterm (41450Ah)

    00411109  jmp         std::basic_ios<char,std::char_traits<char> >::tie (412834h)

    0041110E  jmp         std::ios_base::width (4127F2h)

    00411113  jmp         GetCurrentProcess (414B5Ah)

    00411118  jmp         Circle::~Circle (411E30h)

    0041111D  jmp         std::basic_streambuf<char,std::char_traits<char> >::sputc (41280Ah)

    00411122  jmp         std::basic_ostream<char,std::char_traits<char> >::operator<< (4127E0h)

    00411127  jmp         _encode_pointer (414100h)

    0041112C  jmp         std::ios_base::width (412822h)

    00411131  jmp         _RTC_UninitUse (413A80h)

    00411136  jmp         _RTC_Shutdown (412AD0h)

    0041113B  jmp         type_info::`vector deleting destructor' (412B10h)

    00411140  jmp         _FindPESection (414320h)

    00411145  jmp         Rectangle::`scalar deleting destructor' (411C20h)

    0041114A  jmp         _configthreadlocale (413E78h)

    0041114F  jmp         _RTC_InitBase (412A90h)

    00411154  jmp         _RTC_StackFailure (413700h)

    00411159  jmp         LoadLibraryA (414B96h)

    0041115E  jmp         RaiseException (414B72h)

    00411163  jmp         _crt_debugger_hook (414550h)

    00411168  jmp         _ValidateImageBase (4142A0h)

    0041116D  jmp         Shape::value (4118F0h)

    00411172  jmp         InterlockedCompareExchange (414B4Eh)

    00411177  jmp         Rectangle::~Rectangle (411C90h)

    0041117C  jmp         Shape::Shape (411A30h)

    00411181  jmp         std::basic_streambuf<char,std::char_traits<char> >::_Lock (41284Ch)

    00411186  jmp         std::char_traits<char>::eof (412804h)

    0041118B  jmp         std::basic_ostream<char,std::char_traits<char> >::sentry::~sentry (412560h)

    00411190  jmp         Shape::~Shape (411B60h)

    00411195  jmp         GetProcessHeap (414BCCh)

    0041119A  jmp         _RTC_SetErrorFuncW (413C60h)

    0041119F  jmp         _onexit (413FA0h)

    004111A4  jmp         NtCurrentTeb (412FF0h)

    004111A9  jmp         HeapFree (414BC0h)

    004111AE  jmp         std::operator<<<std::char_traits<char> > (411E90h)

    004111B3  jmp         _RTC_SetErrorFunc (413C30h)

    004111B8  jmp         _invoke_watson_if_error (413ED0h)

    004111BD  jmp         std::basic_ostream<char,std::char_traits<char> >::operator<< (4127DAh)

    004111C2  jmp         std::basic_ostream<char,std::char_traits<char> >::_Sentry_base::~_Sentry_base (412730h)

    004111C7  jmp         TerminateProcess (414B54h)

    004111CC  jmp         std::basic_ostream<char,std::char_traits<char> >::flush (41282Eh)

    004111D1  jmp         mainCRTStartup (412CF0h)

    004111D6  jmp         QueryPerformanceCounter (414B9Ch)

    004111DB  jmp         __p__commode (413F8Eh)

    004111E0  jmp         _unlock (414B24h)

    004111E5  jmp         GetCurrentProcessId (414BAEh)

    004111EA  jmp         _RTC_CheckStackVars2 (412980h)

    004111EF  jmp         __set_app_type (414106h)

    004111F4  jmp         _purecall (412BB4h)

    004111F9  jmp         _RTC_CheckEsp (412890h)

    004111FE  jmp         main (4115B0h)

    00411203  jmp         Rectangle::`scalar deleting destructor' (411C20h)

    00411208  jmp         _RTC_Initialize (413F30h)

    0041120D  jmp         _controlfp_s (414B18h)

    00411212  jmp         GetSystemTimeAsFileTime (414BB4h)

    00411217  jmp         _decode_pointer (414B36h)

    0041121C  jmp         _invoke_watson (414B1Eh)

    00411221  jmp         _RTC_GetSrcLine (414560h)

    00411226  jmp         _CRT_RTC_INITW (413CA6h)

    0041122B  jmp         GetTickCount (414BA2h)

    00411230  jmp         std::basic_streambuf<char,std::char_traits<char> >::sputn (4127F8h)

    00411235  jmp         _IsNonwritableInCurrentImage (4143B0h)

    0041123A  jmp         __CxxFrameHandler3 (41286Ah)

    0041123F  jmp         HeapAlloc (414BC6h)

    00411244  jmp         _amsg_exit (41410Ch)

    00411249  jmp         operator new (412864h)

    0041124E  jmp         _XcptFilter (414286h)

    00411253  jmp         _CrtSetCheckCount (414298h)

    00411258  jmp         InterlockedExchange (414B42h)

    0041125D  jmp         UnhandledExceptionFilter (414B60h)

    00411262  jmp         std::basic_ostream<char,std::char_traits<char> >::sentry::sentry (412400h)

    00411267  jmp         type_info::type_info (412AF0h)

    0041126C  jmp         printf (41285Eh)

    00411271  jmp         Circle::Circle (411CF0h)

    00411276  jmp         _except_handler4_common (414B3Ch)

    0041127B  jmp         _matherr (413F10h)

    00411280  jmp         std::basic_ios<char,std::char_traits<char> >::fill (412816h)

    00411285  jmp         __getmainargs (414112h)

    0041128A  jmp         __ArrayUnwind (413D90h)

    0041128F  jmp         Circle::area (411D70h)

    00411294  jmp         lstrlenA (414B8Ah)

    00411299  jmp         _RTC_Failure (413300h)

    0041129E  jmp         std::ios_base::flags (41281Ch)

    004112A3  jmp         _RTC_AllocaFailure (413870h)

    004112A8  jmp         Shape::`scalar deleting destructor' (411AF0h)

    004112AD  jmp         DebuggerKnownHandle (413230h)

    004112B2  jmp         exit (414292h)

    004112B7  jmp         std::basic_ostream<char,std::char_traits<char> >::_Sentry_base::_Sentry_base (412670h)

    004112BC  jmp         __dllonexit (414B2Ah)

    004112C1  jmp         FreeLibrary (414BDEh)

    004112C6  jmp         `eh vector destructor iterator' (413CB0h)

    004112CB  jmp         _initterm_e (414510h)

    004112D0  jmp         std::basic_ostream<char,std::char_traits<char> >::_Osfx (412840h)

    004112D5  jmp         _RTC_GetErrorFunc (413C90h)

    它表示程序跳转到地址0x412BB4,下面列出的0x00412BB4中的代码,其中,我们可以看到它是间接寻址。它将跳转到0x0041B418的内容。

    _purecall:

    00412BB4  jmp         dword ptr [__imp___purecall (41B418h)]

    从下图可以看出,0x0041B418的内容是0x102527f0,这是purecall的起始地址。

    我们继续执行步骤,然后,它将跳到0x102527f0,即purecall的起始地址。从下图中,我们可以清楚地看到它。

    purecall的反汇编代码如下

    void __cdecl _purecall(
    
            void
    
            )
    
    {
    
    102527F0  push        ebp 
    
    102527F1  mov         ebp,esp
    
    102527F3  push        ecx 
    
        _purecall_handler purecall = (_purecall_handler) _decode_pointer(__pPurecall);
    
    102527F4  mov         eax,dword ptr [___pPurecall (10313144h)]
    
    102527F9  push        eax 
    
    102527FA  call        _decode_pointer (10204900h)
    
    102527FF  add         esp,4
    
    10252802  mov         dword ptr [purecall],eax
    
        if(purecall != NULL)
    
    10252805  cmp         dword ptr [purecall],0
    
    10252809  je          _purecall+1Eh (1025280Eh)
    
        {
    
            purecall();
    
    1025280B  call        dword ptr [purecall]
    
     
    
            /*  shouldn't return, but if it does, we drop back to
    
                default behaviour
    
            */
    
        }
    
     
    
        _NMSG_WRITE(_RT_PUREVIRT);
    
    1025280E  push        19h 
    
    10252810  call        _NMSG_WRITE (10202AA0h)
    
    10252815  add         esp,4
    
        /* do not write the abort message */
    
        _set_abort_behavior(0, _WRITE_ABORT_MSG);
    
    10252818  push        1   
    
    1025281A  push           
    
    1025281C  call        _set_abort_behavior (10218780h)
    
    10252821  add         esp,8
    
        abort();
    
    10252824  call        abort (10218640h)
    
    }
    
    10252829  mov         esp,ebp
    
    1025282B  pop         ebp 
    
    1025282C  ret   

    源代码如下:D:Program Files (x86)Microsoft Visual Studio 12.0VCcrtsrcpurevirt.c

        /////////////////////////////////////////////////////////////////////////////  
        //  
        // The global variable:  
        //  
          
        extern _purecall_handler __pPurecall;  
          
        /*** 
        *void _purecall(void) - 
        * 
        *Purpose: 
        *       The compiler calls this if a pure virtual happens 
        * 
        *Entry: 
        *       No arguments 
        * 
        *Exit: 
        *       Never returns 
        * 
        *Exceptions: 
        * 
        *******************************************************************************/  
          
        void __cdecl _purecall(  
                void  
                )  
        {  
            _purecall_handler purecall = (_purecall_handler) _decode_pointer(__pPurecall);  
            if(purecall != NULL)  
            {  
                purecall();  
          
                /*  shouldn't return, but if it does, we drop back to 
                    default behaviour 
                */  
            }  
          
            _NMSG_WRITE(_RT_PUREVIRT);  
            /* do not write the abort message */  
            _set_abort_behavior(0, _WRITE_ABORT_MSG);  
            abort();  
        }  

    弹出提示框的消息来源如下

    _RT_PUREVIRT 宏

    //file: src/rterr.h
    
    #define _RT_PUREVIRT   25    /* pure virtual function call attempted (C++ error) */

    _RT_PUREVIRT_TXT 宏

    //file: src/cmsgs.h
    
    #define EOL "/r/n"
    
    #define _RT_PUREVIRT_TXT   "R6025" EOL "- pure virtual function call" EOL
    消息列表
    //file: src/crt0msg.c
    * struct used to lookup and access runtime error messages */ struct rterrmsgs { int rterrno; /* error number */ char *rterrtxt; /* text of error message */ }; /* runtime error messages */ static struct rterrmsgs rterrs[] = { /* 2 */ { _RT_FLOAT, _RT_FLOAT_TXT }, /* 8 */ { _RT_SPACEARG, _RT_SPACEARG_TXT }, /* 9 */ { _RT_SPACEENV, _RT_SPACEENV_TXT }, /* 10 */ { _RT_ABORT, _RT_ABORT_TXT }, /* 16 */ { _RT_THREAD, _RT_THREAD_TXT }, /* 17 */ { _RT_LOCK, _RT_LOCK_TXT }, /* 18 */ { _RT_HEAP, _RT_HEAP_TXT }, /* 19 */ { _RT_OPENCON, _RT_OPENCON_TXT }, /* 22 */ /* { _RT_NONCONT, _RT_NONCONT_TXT }, */ /* 23 */ /* { _RT_INVALDISP, _RT_INVALDISP_TXT }, */ /* 24 */ { _RT_ONEXIT, _RT_ONEXIT_TXT }, /* 25 */ { _RT_PUREVIRT, _RT_PUREVIRT_TXT }, /* 26 */ { _RT_STDIOINIT, _RT_STDIOINIT_TXT }, /* 27 */ { _RT_LOWIOINIT, _RT_LOWIOINIT_TXT }, /* 28 */ { _RT_HEAPINIT, _RT_HEAPINIT_TXT }, ///* 29 */ //{ _RT_BADCLRVERSION, _RT_BADCLRVERSION_TXT }, /* 30 */ { _RT_CRT_NOTINIT, _RT_CRT_NOTINIT_TXT }, /* 31 */ { _RT_CRT_INIT_CONFLICT, _RT_CRT_INIT_CONFLICT_TXT}, /* 32 */ { _RT_LOCALE, _RT_LOCALE_TXT}, /* 33 */ { _RT_CRT_INIT_MANAGED_CONFLICT, _RT_CRT_INIT_MANAGED_CONFLICT_TXT}, /* 34 */ { _RT_CHECKMANIFEST, _RT_CHECKMANIFEST_TXT}, ///* 35 - not for _NMSG_WRITE, text passed directly to FatalAppExit */ //{ _RT_COOKIE_INIT, _RT_COOKIE_INIT_TXT}, /* 120 */ { _RT_DOMAIN, _RT_DOMAIN_TXT }, /* 121 */ { _RT_SING, _RT_SING_TXT }, /* 122 */ { _RT_TLOSS, _RT_TLOSS_TXT }, /* 252 */ { _RT_CRNL, _RT_CRNL_TXT }, /* 255 */ { _RT_BANNER, _RT_BANNER_TXT } }; /* number of elements in rterrs[] */ #define _RTERRCNT ( sizeof(rterrs) / sizeof(struct rterrmsgs) )

    这可以从以下从调试中捕获的图中进行验证。

    哪个函数提示消息?

    //file: src/crt0msg.c
    
     
    
    /***
    
    *__NMSG_WRITE(message) - write a given message to handle 2 (stderr)
    
    *
    
    *Purpose:
    
    *       This routine writes the message associated with rterrnum
    
    *       to stderr.
    
    *
    
    *Entry:
    
    *       int rterrnum - runtime error number
    
    *
    
    *Exit:
    
    *       no return value
    
    *
    
    *Exceptions:
    
    *       none
    
    *
    
    *******************************************************************************/
    
     
    
    void __cdecl _NMSG_WRITE (
    
            int rterrnum
    
            )
    
    {
    
            int tblindx;
    
            DWORD bytes_written;            /* bytes written */
    
     
    
            for ( tblindx = 0 ; tblindx < _RTERRCNT ; tblindx++ )
    
                if ( rterrnum == rterrs[tblindx].rterrno )    //in rterrs array, find the mapped message
    
                    break;
    
     
    
            if ( tblindx < _RTERRCNT )
    
            {
    
    #ifdef _DEBUG
    
                /*
    
                 * Report error.
    
                 *
    
                 * If _CRT_ERROR has _CRTDBG_REPORT_WNDW on, and user chooses
    
                 * "Retry", call the debugger.
    
                 *
    
                 * Otherwise, continue execution.
    
                 *
    
                 */
    
     
    
                if (rterrnum != _RT_CRNL && rterrnum != _RT_BANNER && rterrnum != _RT_CRT_NOTINIT)
    
                {
    
                    if (1 == _CrtDbgReport(_CRT_ERROR, NULL, 0, NULL, rterrs[tblindx].rterrtxt))
    
                        _CrtDbgBreak();
    
                }
    
    #endif  /* _DEBUG */
    
                if ( (_set_error_mode(_REPORT_ERRMODE) == _OUT_TO_STDERR) ||
    
                     ((_set_error_mode(_REPORT_ERRMODE) == _OUT_TO_DEFAULT) &&
    
                      (__app_type == _CONSOLE_APP)) )
    
                {
    
                    HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
    
                    if (hStdErr && hStdErr!=INVALID_HANDLE_VALUE)
    
                    {
    
                        WriteFile( hStdErr,
    
                                      rterrs[tblindx].rterrtxt,
    
                                      (unsigned long)strlen(rterrs[tblindx].rterrtxt),
    
                                      &bytes_written,
    
                                      NULL );
    
                    }
    
                }
    
                else if (rterrnum != _RT_CRNL)
    
                {
    
                    #define MSGTEXTPREFIX "Runtime Error!/n/nProgram: "
    
                    static char outmsg[sizeof(MSGTEXTPREFIX) + _MAX_PATH + 2 + 500];
    
                        // runtime error msg + progname + 2 newline + runtime error text.
    
                    char * progname = &outmsg[sizeof(MSGTEXTPREFIX)-1];
    
                    size_t progname_size = _countof(outmsg) - (progname - outmsg);
    
                    char * pch = progname;
    
     
    
                    _ERRCHECK(strcpy_s(outmsg, _countof(outmsg), MSGTEXTPREFIX));
    
     
    
                    progname[MAX_PATH] = '/0';
    
                    if (!GetModuleFileName(NULL, progname, MAX_PATH))
    
                        _ERRCHECK(strcpy_s(progname, progname_size, "<program name unknown>"));
    
     
    
                    #define MAXLINELEN 60
    
                    if (strlen(pch) + 1 > MAXLINELEN)
    
                    {
    
                        pch += strlen(progname) + 1 - MAXLINELEN;
    
                        _ERRCHECK(strncpy_s(pch, progname_size - (pch - progname), "...", 3));
    
                    }
    
     
    
                    _ERRCHECK(strcat_s(outmsg, _countof(outmsg), "/n/n"));
    
                    _ERRCHECK(strcat_s(outmsg, _countof(outmsg), rterrs[tblindx].rterrtxt));
    
     
    
                    __crtMessageBoxA(outmsg,
    
                            "Microsoft Visual C++ Runtime Library",
    
                            MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
    
                }
    
            }
    
    }

    整个调用栈如下:

    当纯虚函数显式实现时是什么情况?

    在类内实现它

    class Shape
    
    {
    
    ...
    
    public:
    
        virtual double area() const = 0
    
        {
    
            std::cout << "pure virtual area() called" << std::endl;
    
            return 0;
    
    }
    
    ...
    
    };

    没有编译器错误,但会提示“pure virtual function call”

    本文通过一个典型的例子,详细说明了在win32平台的构造函数/析构函数中直接/间接调用纯虚函数时的程序本身。列出了一些msvc-crt源代码,分析了purecall函数及其反汇编代码。此外,我们还介绍了该程序的跳转表,以及提示消息的来源,该跳转表是在数组(rterrs)和一些宏中定义的,例如_RT_PUREVIRT和_RT_PUREVIRT_TXT。最后,我们给出了纯虚函数的两个实现来验证它是否工作,从结果中我们发现,即使有纯虚函数的实现,编译器也会显式忽略实现的代码,仍然会调用pure call,并提示“pure virtual function call”。

     
     
  • 相关阅读:
    HDU 4348 To the moon(可持久化线段树)
    HDU 5875 Function 大连网络赛 线段树
    HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)
    HDU 5876 大连网络赛 Sparse Graph
    HDU 5701 中位数计数 百度之星初赛
    CodeForces 708B Recover the String
    Java实现 蓝桥杯 算法提高 套正方形(暴力)
    ASP.NET生成验证码
    ASP.NET生成验证码
    ASP.NET生成验证码
  • 原文地址:https://www.cnblogs.com/yilang/p/11825661.html
Copyright © 2011-2022 走看看