深入解析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;
}
编译执行上面的代码,报
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] mov ecx,33h mov eax,0CCCCCCCCh 0041190D rep stos dword ptr es:[edi] 0041190F pop ecx mov dword ptr [ebp-],ecx return ValuePerSquareUnit * area(); mov eax,dword ptr [this] //eax = 0x003b5fc0, move ‘this’ pointer to eax mov edx,dword ptr [eax] //edx = 0x00417800, move vfptr to edx 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 cmp esi,esp call @ILT+(__RTC_CheckEsp) (4111F9h) mov ecx,dword ptr [this] 0041192B fmul qword ptr [ecx+] } 0041192E pop edi 0041192F pop esi pop ebx add esp,0CCh cmp ebp,esp call @ILT+(__RTC_CheckEsp) (4111F9h) 0041193E mov esp,ebp pop ebp 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)]
我们继续执行步骤,然后,它将跳到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);
02527F4 mov eax,dword ptr [___pPurecall (10313144h)] 102527F9 push eax 102527FA call _decode_pointer (10204900h) 102527FF add esp, mov dword ptr [purecall],eax if(purecall != NULL) cmp dword ptr [purecall], je _purecall+1Eh (1025280Eh) { purecall();
025280B 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.0\VC\crt\src\purevirt.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(, _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 = ; 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 ( == _CrtDbgReport(_CRT_ERROR, NULL, , 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 + + ]; // runtime error msg + progname + 2 newline + runtime error text. char * progname = &outmsg[sizeof(MSGTEXTPREFIX)-]; 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) + > MAXLINELEN) { pch += strlen(progname) + - MAXLINELEN; _ERRCHECK(strncpy_s(pch, progname_size - (pch - progname), "...", )); } _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 = { std::cout << "pure virtual area() called" << std::endl; return ; } ... };
没有编译器错误,但会提示“pure virtual function call”
本文通过一个典型的例子,详细说明了在win32平台的构造函数/析构函数中直接/间接调用纯虚函数时的程序本身。列出了一些msvc-crt源代码,分析了purecall函数及其反汇编代码。此外,我们还介绍了该程序的跳转表,以及提示消息的来源,该跳转表是在数组(rterrs)和一些宏中定义的,例如_RT_PUREVIRT和_RT_PUREVIRT_TXT。最后,我们给出了纯虚函数的两个实现来验证它是否工作,从结果中我们发现,即使有纯虚函数的实现,编译器也会显式忽略实现的代码,仍然会调用pure call,并提示“pure virtual function call”。
深入解析pure virtual function call的更多相关文章
- pure virtual function call
2015-04-08 10:58:19 基类中定义了纯虚函数,派生类中将其实现. 如果在基类的构造函数或者析构函数中调用了改纯虚函数, 则会出现R6205 Error: pure virtual fu ...
- Mindjet MindManager 2012 从模板创建出现“Runtime Error pure virtual function call” 解决方法
我的Mindjet MindManager 2012 Pro也就是MindManager10 在应用模板之后总会显示 Microsoft Visual C++ Runtime Library Runt ...
- why pure virtual function has definition 为什么可以在基类中实现纯虚函数
看了会音频,无意搜到一个frameworks/base/include/utils/Flattenable.h : virtual ~Flattenable() = 0; 所以查了下“纯虚函数定义实现 ...
- [C++] Pure Virtual Function and Abstract Class
Pure Virtual Function Abstract Class
- 纯虚函数(pure virtual function )和抽象类(abstract base class)
函数体=0的虚函数称为“纯虚函数”.包含纯虚函数的类称为“抽象类” #include <string> class Animal // This Animal is an abstract ...
- 结合实例详解"pure Virtual function called"
作者:阿波 链接:http://blog.csdn.net/livelylittlefish/article/details/9750593 (4年前的一篇文章,翻出来共享一下.) 本实例即为经典的讲 ...
- c++ virtual 和 pure virtual的区别
参考资料: http://stackoverflow.com/questions/1306778/c-virtual-pure-virtual-explained 验证代码: #include < ...
- OD: Windows Security Techniques & GS Bypassing via C++ Virtual Function
Windows 安全机制 漏洞的万源之本在于冯诺依曼设计的计算机模型没有将代码和数据进行区分——病毒.加壳脱壳.shellcode.跨站脚本攻击.SQL注入等都是因为计算机把数据和代码混淆这一天然缺陷 ...
- (转) Virtual function
原文地址:http://en.wikipedia.org/wiki/Virtual_function In object-oriented programming, a virtual functio ...
随机推荐
- 【leetcode-11】盛最多水的容器
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) .在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0).找出其中的两条线, ...
- Maven 初学+http://mvnrepository.com/
了解 maven是一款服务于java平台的自动化构建工具(项目管理工具) 构建:全方位.多角度.深层次地建立 项目构建是一个项目从:源代码.编译.测试.打包.部署.运行的过程 用来解决团队开发遇到的问 ...
- Spring Boot Freemarker特别篇之contextPath【从零开始学Spring Boot
需求缘起:有人在群里@我:请教群主大神一个问题,spring boot + freemarker 怎么获取contextPath 头疼死我了,网上没一个靠谱的 .我就看看之前博客中的 [Spri ...
- .NET Core的SqlSugar上手使用小例子
开始直接建个空的WEB项目-建Controllers文件夹-开启MVC-添加NuGet程序包SqlSugarCore public class Startup { // This method get ...
- 读取txt文件的数据,并将其转换为矩阵
import numpy as nppath_txt_data = 'C:/Users/51102/Desktop/my_yolo/data/box/train.txt'def input_data( ...
- JAVA案例练习: 去除ArrayList中重复的字符串(字符串内容相同),去除重复的对象
package com.yqw.list; import java.util.ArrayList;import java.util.Iterator; public class Demo_ArrayL ...
- 小div在大div中垂直居中方式
代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...
- CI隐藏入口文件index.php
1.需要apache打开rewrite_module,然后修改httpd.conf的AllowOverride none 为AllowOverride All(里面,不同的环境目录不同) 2.在CI的 ...
- CSS-3D动画笔记
3D 在2d的基础上添加 z 轴的变化 3D 位移:在2d的基础上添加 translateZ(),或者使用translate3d() translateZ():以方框中心为原点,变大 3D 缩放:在2 ...
- java自定义注释及其信息提取
转自:https://xuwenjin666.iteye.com/blog/1637247 1. 自定义注解 import java.lang.annotation.Retention; import ...