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”。

     
     
  • 相关阅读:
    将springboot安装成windows服务启动。
    jackson将json数组转成List、普通数组。
    maven编译正常,运行报错:中没有主清单属性
    [SQL]SUTFF内置函数的用法 (删除指定长度的字符并在指定的起始点插入另一组字符)
    sql语句中charindex的用法 可用于截取字符串
    C# 中怎么将string转换成int型
    C#判断奇偶数的函數
    asp.net 下载Excel (数据流,不保存)--客户端
    C# DateTime 日期加1天 减一天 加一月 减一月 等方法(转)
    ASP.NET jquery ajax传递参数
  • 原文地址:https://www.cnblogs.com/yilang/p/11825661.html
Copyright © 2011-2022 走看看