code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; word-wrap: normal; line-height: 1.4; -moz-tab-size: 8; -o-tab-size: 8; tab-size: 8; -webkit-hyphens: none; -moz-hyphens: none; -ms-hyphens: none; hyphens: none }
pre[class*="language-"] { padding: 0.8em; overflow: auto; border-radius: 3px; background: rgba(245, 245, 245, 1) }
:not(pre)>code[class*="language-"] { padding: 0.1em; border-radius: 0.3em; white-space: normal; background: rgba(245, 245, 245, 1) }
.token.comment, .token.blockquote { color: rgba(150, 152, 150, 1) }
.token.cdata { color: rgba(24, 54, 145, 1) }
.token.doctype, .token.punctuation, .token.variable, .token.macro.property { color: rgba(51, 51, 51, 1) }
.token.operator, .token.important, .token.keyword, .token.rule, .token.builtin { color: rgba(167, 29, 93, 1) }
.token.string, .token.url, .token.regex, .token.attr-value { color: rgba(24, 54, 145, 1) }
.token.property, .token.number, .token.boolean, .token.entity, .token.atrule, .token.constant, .token.symbol, .token.command, .token.code { color: rgba(0, 134, 179, 1) }
.token.tag, .token.selector, .token.prolog { color: rgba(99, 163, 92, 1) }
.token.function, .token.namespace, .token.pseudo-element, .token.class, .token.class-name, .token.pseudo-class, .token.id, .token.url-reference .token.variable, .token.attr-name { color: rgba(121, 93, 163, 1) }
.token.entity { cursor: help }
.token.title, .token.title .token.punctuation { font-weight: bold; color: rgba(29, 62, 129, 1) }
.token.list { color: rgba(237, 106, 67, 1) }
.token.inserted { background-color: rgba(234, 255, 234, 1); color: rgba(85, 165, 50, 1) }
.token.deleted { background-color: rgba(255, 236, 236, 1); color: rgba(189, 44, 0, 1) }
.token.bold { font-weight: bold }
.token.italic { font-style: italic }
.language-json .token.property { color: rgba(24, 54, 145, 1) }
.language-markup .token.tag .token.punctuation { color: rgba(51, 51, 51, 1) }
code.language-css, .language-css .token.function { color: rgba(0, 134, 179, 1) }
.language-yaml .token.atrule { color: rgba(99, 163, 92, 1) }
code.language-yaml { color: rgba(24, 54, 145, 1) }
.language-ruby .token.function { color: rgba(51, 51, 51, 1) }
.language-markdown .token.url { color: rgba(121, 93, 163, 1) }
.language-makefile .token.symbol { color: rgba(121, 93, 163, 1) }
.language-makefile .token.variable { color: rgba(24, 54, 145, 1) }
.language-makefile .token.builtin { color: rgba(0, 134, 179, 1) }
.language-bash .token.keyword { color: rgba(0, 134, 179, 1) }
pre[data-line] { position: relative; padding: 1em 0 1em 3em }
pre[data-line] .line-highlight-wrapper { position: absolute; top: 0; left: 0; background-color: rgba(0, 0, 0, 0); display: block; width: 100% }
pre[data-line] .line-highlight { position: absolute; left: 0; right: 0; margin-top: 1em; background: linear-gradient(90deg, rgba(153, 122, 102, 0.1) 70%, rgba(153, 122, 102, 0)); pointer-events: none; line-height: inherit; white-space: pre }
pre[data-line] .line-highlight:before, pre[data-line] .line-highlight[data-end]:after { content: attr(data-start); position: absolute; top: 0.4em; left: 0.6em; min-width: 1em; padding: 0 0.5em; background-color: rgba(153, 122, 102, 0.4); color: rgba(245, 242, 240, 1); font: bold 65% / 1.5 sans-serif; text-align: center; vertical-align: 0.3em; border-radius: 999px; text-shadow: none; box-shadow: 0 1px rgba(255, 255, 255, 1) }
pre[data-line] .line-highlight[data-end]:after { content: attr(data-end); top: auto; bottom: 0.4em }
html body { font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif; font-size: 16px; line-height: 1.6; color: rgba(51, 51, 51, 1); background-color: rgba(255, 255, 255, 1); overflow: initial; box-sizing: border-box; word-wrap: break-word }
html body>:first-child { margin-top: 0 }
html body h1, html body h2, html body h3, html body h4, html body h5, html body h6 { line-height: 1.2; margin-top: 1em; margin-bottom: 16px; color: rgba(0, 0, 0, 1) }
html body h1 { font-size: 2.25em; font-weight: 300; padding-bottom: 0.3em }
html body h2 { font-size: 1.75em; font-weight: 400; padding-bottom: 0.3em }
html body h3 { font-size: 1.5em; font-weight: 500 }
html body h4 { font-size: 1.25em; font-weight: 600 }
html body h5 { font-size: 1.1em; font-weight: 600 }
html body h6 { font-size: 1em; font-weight: 600 }
html body h1, html body h2, html body h3, html body h4, html body h5 { font-weight: 600 }
html body h5 { font-size: 1em }
html body h6 { color: rgba(92, 92, 92, 1) }
html body strong { color: rgba(0, 0, 0, 1) }
html body del { color: rgba(92, 92, 92, 1) }
html body a:not([href]) { color: inherit; text-decoration: none }
html body a { color: rgba(0, 136, 204, 1); text-decoration: none }
html body a:hover { color: rgba(0, 163, 245, 1); text-decoration: none }
html body img { max-width: 100% }
html body>p { margin-top: 0; margin-bottom: 16px; word-wrap: break-word }
html body>ul, html body>ol { margin-bottom: 16px }
html body ul, html body ol { padding-left: 2em }
html body ul.no-list, html body ol.no-list { padding: 0; list-style-type: none }
html body ul ul, html body ul ol, html body ol ol, html body ol ul { margin-top: 0; margin-bottom: 0 }
html body li { margin-bottom: 0 }
html body li.task-list-item { list-style: none }
html body li>p { margin-top: 0; margin-bottom: 0 }
html body .task-list-item-checkbox { margin: 0 0.2em 0.25em -1.8em; vertical-align: middle }
html body .task-list-item-checkbox:hover { cursor: pointer }
html body blockquote { margin: 16px 0; font-size: inherit; padding: 0 15px; color: rgba(92, 92, 92, 1); border-left: 4px solid rgba(214, 214, 214, 1) }
html body blockquote>:first-child { margin-top: 0 }
html body blockquote>:last-child { margin-bottom: 0 }
html body hr { height: 4px; margin: 32px 0; background-color: rgba(214, 214, 214, 1); border: 0 none }
html body table { margin: 10px 0 15px; border-collapse: collapse; border-spacing: 0; display: block; width: 100%; overflow: auto; word-break: keep-all }
html body table th { font-weight: bold; color: rgba(0, 0, 0, 1) }
html body table td, html body table th { border: 1px solid rgba(214, 214, 214, 1); padding: 6px 13px }
html body dl { padding: 0 }
html body dl dt { padding: 0; margin-top: 16px; font-size: 1em; font-style: italic; font-weight: bold }
html body dl dd { padding: 0 16px; margin-bottom: 16px }
html body code { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.85em !important; color: rgba(0, 0, 0, 1); background-color: rgba(240, 240, 240, 1); border-radius: 3px; padding: 0.2em 0 }
html body code::before, html body code::after { letter-spacing: -0.2em; content: " " }
html body pre>code { padding: 0; margin: 0; font-size: 0.85em !important; word-break: normal; white-space: pre; background: rgba(0, 0, 0, 0); border: 0 }
html body .highlight { margin-bottom: 16px }
html body .highlight pre, html body pre { padding: 1em; overflow: auto; font-size: 0.85em !important; line-height: 1.45; border: rgba(214, 214, 214, 1); border-radius: 3px }
html body .highlight pre { margin-bottom: 0; word-break: normal }
html body pre code, html body pre tt { display: inline; max-width: initial; padding: 0; margin: 0; overflow: initial; line-height: inherit; word-wrap: normal; background-color: rgba(0, 0, 0, 0); border: 0 }
html body pre code:before, html body pre tt:before, html body pre code:after, html body pre tt:after { content: normal }
html body p, html body blockquote, html body ul, html body ol, html body dl, html body pre { margin-top: 0; margin-bottom: 16px }
html body kbd { color: rgba(0, 0, 0, 1); border-top: 1px solid rgba(214, 214, 214, 1); border-right: 1px solid rgba(214, 214, 214, 1); border-bottom: 2px solid rgba(199, 199, 199, 1); border-left: 1px solid rgba(214, 214, 214, 1); padding: 2px 4px; background-color: rgba(240, 240, 240, 1); border-radius: 3px }
@media print { html body { background-color: rgba(255, 255, 255, 1) } html body h1, html body h2, html body h3, html body h4, html body h5, html body h6 { color: rgba(0, 0, 0, 1); page-break-after: avoid } html body blockquote { color: rgba(92, 92, 92, 1) } html body pre { page-break-inside: avoid } html body table { display: table } html body img { display: block; max-width: 100%; max-height: 100% } html body pre, html body code { word-wrap: break-word; white-space: pre } }
.markdown-preview { width: 100%; height: 100%; box-sizing: border-box }
.markdown-preview .pagebreak, .markdown-preview .newpage { page-break-before: always }
.markdown-preview pre.line-numbers { position: relative; padding-left: 3.8em; counter-reset: linenumber 0 }
.markdown-preview pre.line-numbers>code { position: relative }
.markdown-preview pre.line-numbers .line-numbers-rows { position: absolute; pointer-events: none; top: 1em; font-size: 100%; left: 0; width: 3em; letter-spacing: -1px; border-right: 1px solid rgba(153, 153, 153, 1); -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none }
.markdown-preview pre.line-numbers .line-numbers-rows>span { pointer-events: none; display: block; counter-increment: linenumber 1 }
.markdown-preview pre.line-numbers .line-numbers-rows>span:before { content: counter(linenumber); color: rgba(153, 153, 153, 1); display: block; padding-right: 0.8em; text-align: right }
.markdown-preview .mathjax-exps .MathJax_Display { text-align: center !important }
.markdown-preview:not([for="preview"]) .code-chunk .btn-group { display: none }
.markdown-preview:not([for="preview"]) .code-chunk .status { display: none }
.markdown-preview:not([for="preview"]) .code-chunk .output-div { margin-bottom: 16px }
{ width: 8px }
{ border-radius: 10px; background-color: rgba(0, 0, 0, 0) }
{ border-radius: 5px; background-color: rgba(150, 150, 150, 0.66); border: 4px solid rgba(150, 150, 150, 0.66); background-clip: content-box }
html body[for="html-export"]:not([data-presentation-mode]) { position: relative; width: 100%; height: 100%; top: 0; left: 0; margin: 0; padding: 0; overflow: auto }
html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview { position: relative; top: 0 }
@media screen and (min-width: 914px) { html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview { padding: 2em calc(50% - 457px + 2em) } }
@media screen and (max-width: 914px) { html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview { padding: 2em } }
@media screen and (max-width: 450px) { html body[for="html-export"]:not([data-presentation-mode]) .markdown-preview { font-size: 14px !important; padding: 1em } }
@media print { html body[for="html-export"]:not([data-presentation-mode]) #sidebar-toc-btn { display: none } }
html body[for="html-export"]:not([data-presentation-mode]) #sidebar-toc-btn { position: fixed; bottom: 8px; left: 8px; font-size: 28px; cursor: pointer; color: inherit; z-index: 99; width: 32px; text-align: center; opacity: 0.4 }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] #sidebar-toc-btn { opacity: 1 }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc { position: fixed; top: 0; left: 0; width: 300px; height: 100%; padding: 32px 0 48px; font-size: 14px; box-shadow: 0 0 4px rgba(150, 150, 150, 0.33); box-sizing: border-box; overflow: auto; background-color: inherit }
{ width: 8px }
{ border-radius: 10px; background-color: rgba(0, 0, 0, 0) }
{ border-radius: 5px; background-color: rgba(150, 150, 150, 0.66); border: 4px solid rgba(150, 150, 150, 0.66); background-clip: content-box }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc a { text-decoration: none }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc ul { padding: 0 1.6em; margin-top: 0.8em }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc li { margin-bottom: 0.8em }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .md-sidebar-toc ul { list-style-type: none }
html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview { left: 300px; width: calc(100% - 300px); padding: 2em calc(50% - 457px - 150px); margin: 0; box-sizing: border-box }
@media screen and (max-width: 1274px) { html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview { padding: 2em } }
@media screen and (max-width: 450px) { html body[for="html-export"]:not([data-presentation-mode])[html-show-sidebar-toc] .markdown-preview { width: 100% } }
html body[for="html-export"]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .markdown-preview { left: 50%; transform: translateX(-50%) }
html body[for="html-export"]:not([data-presentation-mode]):not([html-show-sidebar-toc]) .md-sidebar-toc { display: none }

单重继承-无虚函数

测试代码如下:

class CBase
{
int m_nTest;
public:
CBase():m_nTest(0)
{
std::cout << "CBase()" << std::endl;
} ~CBase()
{
std::cout << "~CBase()" << std::endl;
} void ShowInfo1() { std::cout << m_nTest + 1 << std::endl; }
void ShowInfo() { std::cout << m_nTest << std::endl; }
void ShowInfo2() { std::cout << m_nTest + 2 << std::endl; }
}; class CDerived:public CBase
{
int m_nTest1;
public:
CDerived() :m_nTest1(1)
{
std::cout << "CDerived()" << std::endl;
} ~CDerived()
{
std::cout << "~CDerived()" << std::endl;
}
}; int main(int argc, char* argv[])
{
CDerived t;
return 0;
}

类对象的内存结构如下:

单重继承-有虚函数

  • 基类中有虚函数,派生类中无新增虚函数,派生类中没有重写父类的虚函数

    测试代码如下:

     class CBase
    {
    int m_nTest;
    public:
    CBase();
    ~CBase(); virtual void ShowInfo1();
    virtual void ShowInfo();
    virtual void ShowInfo2();
    }; CBase::CBase() :m_nTest(0)
    {
    std::cout << "CBase()" << std::endl;
    } CBase::~CBase()
    {
    std::cout << "~CBase()" << std::endl;
    } void CBase::ShowInfo1()
    {
    std::cout << m_nTest + 1 << std::endl;
    } void CBase::ShowInfo()
    {
    std::cout << m_nTest << std::endl;
    } void CBase::ShowInfo2()
    {
    std::cout << m_nTest + 2 << std::endl;
    } class CDerived:public CBase
    {
    int m_nTest1;
    public:
    CDerived();
    ~CDerived();
    }; CDerived::CDerived() :m_nTest1(1)
    {
    std::cout << "CDerived()" << std::endl;
    } CDerived::~CDerived()
    {
    std::cout << "~CDerived()" << std::endl;
    } int main(int argc, char* argv[])
    {
    CDerived t;
    return 0;
    }

    CDerived继承自CBase,所以在CDerived类对象构造的时候先调用CBase的构造函数,此时CBase的构造函数

    会CBase类的虚表指针复制到CDerived对象存放虚表指针的位置(对象开始的四个字节),过程如下:



    可以看出CBase类的数据成员m_nTest还未初始化,所以父类覆盖虚表指针的时间是在类数据成员初始化之前

    CBase类构造函数执行完成后,CDerived的构造函数开始执行:



    通过对比可以发现CDerived类的构造函数执行时再次覆盖了虚表指针,当前的虚表指针指向了CDerived类

    自己的虚表;虽然CDerived类继承了CBase类,但是CDerived类并没有重写父类的虚函数,所以VC++编译器

    将CBase的虚表复制给了CDerived类的虚表

  • 基类中有虚函数,派生类中无新增虚函数,派生类中重写了父类的虚函数

    在Derived类中重写两个父类的虚函数:

     class CDerived:public CBase
    {
    int m_nTest1;
    public:
    CDerived();
    ~CDerived();
    virtual void ShowInfo1();
    virtual void ShowInfo();
    }; void CDerived::ShowInfo1()
    {
    std::cout << " CDerived::ShowIfno1:" << m_nTest1 << std::endl;
    } void CDerived::ShowInfo()
    {
    std::cout << " CDerived::ShowInfo:" << m_nTest1 << std::endl;
    } CDerived::CDerived() :m_nTest1(1)
    {
    std::cout << "CDerived()" << std::endl;
    } CDerived::~CDerived()
    {
    std::cout << "~CDerived()" << std::endl;
    }

    观察CDerived对象构造过程中,虚表变化:

    CBase类的构造函数执行时虚表和虚表指针的变化:



    在CBase类的构造函数执行过程中,对象的虚表指针始终指向CBase类的虚表

    CDerived类的构造函数执行时虚表和虚表指针的变化:



    因为CDerived类重写了前两个虚函数,所以CDerived类的虚表前两个函数指针使用的CDerived自己的虚函

    数地址来填充,第三个虚函数因为没有重写,所以依旧使用CBase实现的虚函数的地址填充

     

    接下来观察下CDervied对象析构的过程:



    可以看出在CDervied的析构函数执行的过程中虚表和虚表指针没有发生变化,看看其反汇编代码:

     CDerived::~CDerived()
    {
    000A2360 55 push ebp
    000A2361 8B EC mov ebp,esp
    000A2363 6A FF push 0FFFFFFFFh
    000A2365 68 B0 6F 0A 00 push 0A6FB0h
    000A236A 64 A1 00 00 00 00 mov eax,dword ptr fs:[00000000h]
    000A2370 50 push eax
    000A2371 81 EC CC 00 00 00 sub esp,0CCh
    000A2377 53 push ebx
    000A2378 56 push esi
    000A2379 57 push edi
    000A237A 51 push ecx
    000A237B 8D BD 28 FF FF FF lea edi,[ebp-0D8h]
    000A2381 B9 33 00 00 00 mov ecx,33h
    000A2386 B8 CC CC CC CC mov eax,0CCCCCCCCh
    000A238B F3 AB rep stos dword ptr es:[edi]
    000A238D 59 pop ecx
    000A238E A1 04 C0 0A 00 mov eax,dword ptr [__security_cookie (0AC004h)]
    000A2393 33 C5 xor eax,ebp
    000A2395 50 push eax
    000A2396 8D 45 F4 lea eax,[ebp-0Ch]
    000A2399 64 A3 00 00 00 00 mov dword ptr fs:[00000000h],eax
    000A239F 89 4D EC mov dword ptr [this],ecx
    000A23A2 8B 45 EC mov eax,dword ptr [this]
    000A23A5 C7 00 60 9B 0A 00 mov dword ptr [eax],offset CDerived::`vftable' (0A9B60h)
    std::cout << "~CDerived()" << std::endl;
    000A23AB 8B F4 mov esi,esp
    000A23AD 68 9B 10 0A 00 push offset std::endl<char,std::char_traits<char> > (0A109Bh)
    000A23B2 68 38 9D 0A 00 push offset string "~CDerived()" (0A9D38h)
    000A23B7 A1 98 D0 0A 00 mov eax,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0AD098h)]
    000A23BC 50 push eax
    000A23BD E8 E5 EF FF FF call std::operator<<<std::char_traits<char> > (0A13A7h)
    000A23C2 83 C4 08 add esp,8
    000A23C5 8B C8 mov ecx,eax
    000A23C7 FF 15 A8 D0 0A 00 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0AD0A8h)]
    000A23CD 3B F4 cmp esi,esp
    000A23CF E8 9E ED FF FF call __RTC_CheckEsp (0A1172h)
    }
    000A23D4 8B 4D EC mov ecx,dword ptr [this]
    000A23D7 E8 3F EF FF FF call CBase::~CBase (0A131Bh)
    000A23DC 8B 4D F4 mov ecx,dword ptr [ebp-0Ch]
    000A23DF 64 89 0D 00 00 00 00 mov dword ptr fs:[0],ecx
    000A23E6 59 pop ecx
    000A23E7 5F pop edi
    000A23E8 5E pop esi
    000A23E9 5B pop ebx
    000A23EA 81 C4 D8 00 00 00 add esp,0D8h
    000A23F0 3B EC cmp ebp,esp
    000A23F2 E8 7B ED FF FF call __RTC_CheckEsp (0A1172h)
    000A23F7 8B E5 mov esp,ebp
    000A23F9 5D pop ebp
    000A23FA C3 ret

    但是从这句汇编代码:

     000A23A5 C7 00 60 9B 0A 00  mov dword ptr [eax],offset CDerived::`vftable' (0A9B60h)
    

    可以看出CDerived类的析构函数在执行时还是做了一次虚表指针覆盖动作,用CDerived的虚表地址赋值给

    对象的虚表指针,但在CDerived类的构造函数执行前对象的虚表指针本来就指向CDerived的虚表,所以感觉

    VC++编译器生成的这句代码是多余的,有可能是因为当前程序版本是dubug版本,为了方便调试就多生成了一

    些代码。

    CDerived类的析构函数执行完成后,在CBase类的析构函数开始执行:



    此时对象的虚表指针被CBase的析构函数置为指向CBase类虚表

  • 基类中有虚函数,派生类中有新增虚函数,并且派生类中重写父类的虚函数

    CDerived代码如下:

     class CDerived:public CBase
    {
    int m_nTest1;
    public:
    CDerived();
    ~CDerived();
    virtual void NewVirtualFuction1();
    virtual void ShowInfo1();
    virtual void ShowInfo();
    virtual void NewVirtualFuction2();
    }; void CDerived::NewVirtualFuction1()
    {
    std::cout << " CDerived::NewVirtualFuction:" << m_nTest1 << std::endl;
    } void CDerived::NewVirtualFuction2()
    {
    std::cout << " CDerived::NewVirtualFuction:" << m_nTest1 << std::endl;
    } void CDerived::ShowInfo1()
    {
    std::cout << " CDerived::ShowIfno1:" << m_nTest1 << std::endl;
    } void CDerived::ShowInfo()
    {
    std::cout << " CDerived::ShowInfo:" << m_nTest1 << std::endl;
    } CDerived::CDerived() :m_nTest1(1)
    {
    std::cout << "CDerived()" << std::endl;
    } CDerived:: ~CDerived()
    {
    std::cout << "~CDerived()" << std::endl;
    }

    先是CBase类的构造函数进行虚表指针覆盖:



    然后CDerived类的构造函数进行虚表指针的覆盖:





    可以VC++编译器在为CDerived类生成虚表时,先将父类的虚表复制一份,如果子类重写了父类的某个虚函数,

    就用子类重写的虚函数的地址覆盖虚表中的对应位置,子类自己新增的虚函数则全部追加到这个副本的尾部,

    最终形成了子类自己的虚表

虚析构

代码如下:

class CBase
{
int m_nTest;
public:
CBase();
virtual ~CBase();
}; CBase::CBase() :m_nTest(0)
{
std::cout << "CBase()" << std::endl;
} CBase::~CBase()
{
std::cout << "~CBase()" << std::endl;
} class CDerived:public CBase
{
int m_nTest1;
public:
CDerived();
~CDerived();
}; CDerived::CDerived() :m_nTest1(1)
{
std::cout << "CDerived()" << std::endl;
} CDerived::~CDerived()
{
std::cout << "~CDerived()" << std::endl;
} int main(int argc, char* argv[])
{
CBase * p = new CDerived;
delete p;
return 0;
}

运行结果:



再来看看其反汇编代码:

delete p;
000D6649 89 85 FC FE FF FF mov dword ptr [ebp-104h],eax
000D664F 8B 8D FC FE FF FF mov ecx,dword ptr [ebp-104h]
000D6655 89 8D 08 FF FF FF mov dword ptr [ebp-0F8h],ecx
000D665B 83 BD 08 FF FF FF 00 cmp dword ptr [ebp-0F8h],0
000D6662 74 15 je main+0C9h (0D6679h)
000D6664 6A 01 push 1
000D6666 8B 8D 08 FF FF FF mov ecx,dword ptr [ebp-0F8h] 000D666C E8 3A AE FF FF call CBase::`scalar deleting destructor' (0D14ABh) 000D6671 89 85 F4 FE FF FF mov dword ptr [ebp-10Ch],eax
000D6677 EB 0A jmp main+0D3h (0D6683h)
000D6679 C7 85 F4 FE FF FF 00 00 00 00 mov dword ptr [ebp-10Ch],0

可以看出当用一个基类指针指向动态分配的子类类对象,然后直接通过这个基类指针释放对象,VC++编译器生成

的代码只调用了基类的析构函数,函数,子类的析构函数并没有被调用,如果子类对象中引用了其它资源,就造成

了资源泄漏;所以如果一个类作为基类,那么其虚函数必须得是虚函数,否则当使用一个基类指针指向一个动态分

配的子类对象,然后通过这个基类指针去释放动态分配的子类对象时,子类的析构函数会被调用.

现在将基类的析构函数声明前加virtual关键字,使其成为虚析构函数,观察构造过程,以及通过基类指针释放

动态分配的子类对象时的过程:





从这里可以看出析构函数是一个特殊的成员函数,如果一个类的析构函数为虚函数,那么继承它的子类的析构函

数也是虚函数.

在看看此时的反汇编代码:

delete p;
012429B6 8B 45 EC mov eax,dword ptr [p] //取出虚表指针
012429B9 89 85 FC FE FF FF mov dword ptr [ebp-104h],eax
012429BF 8B 8D FC FE FF FF mov ecx,dword ptr [ebp-104h]
012429C5 89 8D 08 FF FF FF mov dword ptr [ebp-0F8h],ecx
012429CB 83 BD 08 FF FF FF 00 cmp dword ptr [ebp-0F8h],0
012429D2 74 25 je main+0D9h (012429F9h)
012429D4 8B F4 mov esi,esp
012429D6 6A 01 push 1
012429D8 8B 95 08 FF FF FF mov edx,dword ptr [ebp-0F8h]
012429DE 8B 02 mov eax,dword ptr [edx]
012429E0 8B 8D 08 FF FF FF mov ecx,dword ptr [ebp-0F8h]
012429E6 8B 10 mov edx,dword ptr [eax]
012429E8 FF D2 call edx //调用CDerived类的析构
012429EA 3B F4 cmp esi,esp
012429EC E8 A4 E7 FF FF call __RTC_CheckEsp (01241195h)
012429F1 89 85 F4 FE FF FF mov dword ptr [ebp-10Ch],eax
012429F7 EB 0A jmp main+0E3h (01242A03h)
012429F9 C7 85 F4 FE FF FF 00 00 00 00 mov dword ptr [ebp-10Ch],0

上面代码中只有析构函数是虚函数,所以析构函数的指针位于虚表中的第一项,所以上面汇编代码中的:

012429E6 8B 10                mov         edx,dword ptr [eax]
012429E8 FF D2 call edx

通过虚表调用到CDerived类的析构函数,因为CDerived类继承自CBase类,按照C++标准,子类析构完成后在调用

父类的析构函数,所以VC++编译器直接调用CBase的析构函数的代码插入到CDerived类析构函数的后面,再看下

CDerived类的析构函数的反汇编代码:

CDerived::~CDerived()
{
00F62450 55 push ebp
00F62451 8B EC mov ebp,esp
00F62453 6A FF push 0FFFFFFFFh
00F62455 68 F0 73 F6 00 push 0F673F0h
00F6245A 64 A1 00 00 00 00 mov eax,dword ptr fs:[00000000h]
00F62460 50 push eax
00F62461 81 EC CC 00 00 00 sub esp,0CCh
00F62467 53 push ebx
00F62468 56 push esi
00F62469 57 push edi
00F6246A 51 push ecx
00F6246B 8D BD 28 FF FF FF lea edi,[ebp-0D8h]
00F62471 B9 33 00 00 00 mov ecx,33h
00F62476 B8 CC CC CC CC mov eax,0CCCCCCCCh
00F6247B F3 AB rep stos dword ptr es:[edi]
00F6247D 59 pop ecx
00F6247E A1 04 C0 F6 00 mov eax,dword ptr [__security_cookie (0F6C004h)]
00F62483 33 C5 xor eax,ebp
00F62485 50 push eax
00F62486 8D 45 F4 lea eax,[ebp-0Ch]
00F62489 64 A3 00 00 00 00 mov dword ptr fs:[00000000h],eax
00F6248F 89 4D EC mov dword ptr [this],ecx
00F62492 8B 45 EC mov eax,dword ptr [this]
00F62495 C7 00 54 9B F6 00 mov dword ptr [eax],offset CDerived::`vftable' (0F69B54h)
std::cout << "~CDerived()" << std::endl;
00F6249B 8B F4 mov esi,esp
00F6249D 68 96 10 F6 00 push offset std::endl<char,std::char_traits<char> > (0F61096h)
00F624A2 68 64 9B F6 00 push offset string "~CDerived()" (0F69B64h)
00F624A7 A1 98 D0 F6 00 mov eax,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0F6D098h)]
00F624AC 50 push eax
00F624AD E8 59 EF FF FF call std::operator<<<std::char_traits<char> > (0F6140Bh)
00F624B2 83 C4 08 add esp,8
00F624B5 8B C8 mov ecx,eax
00F624B7 FF 15 A4 D0 F6 00 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0F6D0A4h)]
00F624BD 3B F4 cmp esi,esp
00F624BF E8 D1 EC FF FF call __RTC_CheckEsp (0F61195h)
} /**********VC++编译器将调用CBase的代码偷偷插入到CDerived析构函数的尾部***************/
00F624C4 8B 4D EC mov ecx,dword ptr [this]
00F624C7 E8 02 F0 FF FF call CBase::~CBase (0F614CEh)
/********************************************************************************/
00F624CC 8B 4D F4 mov ecx,dword ptr [ebp-0Ch]
00F624CF 64 89 0D 00 00 00 00 mov dword ptr fs:[0],ecx
00F624D6 59 pop ecx
00F624D7 5F pop edi
00F624D8 5E pop esi
00F624D9 5B pop ebx
00F624DA 81 C4 D8 00 00 00 add esp,0D8h
00F624E0 3B EC cmp ebp,esp
00F624E2 E8 AE EC FF FF call __RTC_CheckEsp (0F61195h)
00F624E7 8B E5 mov esp,ebp
00F624E9 5D pop ebp
00F624EA C3 ret

总结:

  • 如果一个类有虚函数,那么这个VC++编译器一定会为其生成虚表,所有对象共用这一个虚表
  • 调用父类构造函数和析构函数期间对象的虚表指针指向父类的虚表,调用子类构造函数后虚表指针指向

    子类自己的虚表,构造和析构期间都会做虚表指针的覆盖动作
  • 一个类如果要作为基类,那么其析构函数一定得是虚函数,以免使用基类指针或者应用释放指向子类对象时

    造成资源泄漏
  • 析构函数是一个特殊的成员函数,如果父类的析构函数是虚函数,那么子类的析构函数也是虚函数
  • 无论是VC++编译器还是其它的C++编译器,为了实现C++标准定义的行为,都会偷偷生成代码

C++单重继承分析的更多相关文章

  1. ActiveRecord-连接多张表之单表继承

    ActiveRecord-连接多张表之单表继承 1. 基本概念 Rails提供了两种机制,可以将复杂的面向对象模型映射为关系模型,即所谓的单表继承(single-table inheritance)和 ...

  2. SpringData-Redis发布订阅自动重连分析

    SpringData-Redis发布订阅自动重连分析 RedisMessageListenerContainer 配置 @Bean @Autowired RedisMessageListenerCon ...

  3. python flask_sqlalchemy 多态 polymorphic 实现单表继承

    sqlalchemy 多态 polymorphic 实现单表继承 sqlaclchemy中的单表继续就是以一个模型类为基类,其他模型类继承基类,所有模型类的的数据都存一张表里面(也可以是多张,只不过基 ...

  4. 【转】NG:垂枝桦基因组图谱构建(2+3组装)及重测序分析

    转自希望组公众号.学习二代+三代组装策略的流程 垂枝桦(Betula pendula)是一种速生乔木,能在短短一年时间内开花,木质坚实,可做细工.家具等,经济价值极高.近日,芬兰研究人员对垂枝桦自交系 ...

  5. caffe可重入单例机制分析

    一个函数可重入是指该函数可以被多个线程同时调用.大多数函数都不是可重如的,因为很多函数会修改静态数据结构里的内容,如果多个线程同时调用,势必破坏共享的静态结构.可以在不改变公共接口的情况下,将一个非重 ...

  6. C++继承分析

    面向对象的三大特性之一就是继承,继承运行我么重用基类中已经存在的内容,这样就简化了代码的编写工作.继承中有三种继承方式即:public protected private,这三种方式规定了不同的访问权 ...

  7. MD4C/CO46/MD04一个很棒的工单缺料分析

    大家好~~~ 之前在MD04物料分析的时候,看到有订单预留,双击有个订单报告可以显示一个订单物料是否缺料清单 这个单独的工单分析可以在T-code:MD4C,CO46查看,如果只是单独的使用,那么这两 ...

  8. java继承分析

    把java学完之后有開始了一遍突然发现对于继承还是不太理解所以就做了一个測试来分析一下 <span style="font-size:18px;">class A{ p ...

  9. C++_代码重用4-多重继承

    继承使用时要注意,默认是私有派生.所以要公有派生时必须记得加关键字Public. MI(Multi Inheritance)会带来哪些问题?以及如何解决它们? 两个主要问题: 从两个不同的基类继承同名 ...

随机推荐

  1. 如何实现一个简易版的 Spring - 如何实现 @Component 注解

    前言 前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入.如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文 ...

  2. dragable tabs & iframe & new window

    dragable tabs & iframe & new window https://www.npmjs.com/package/react-draggable-tab demo h ...

  3. Flutter 中不得不会的 mixin

    mixin 是 Dart 中非常重要的概念,对于未接触过此概念的Coder来说尤其重要,最近看源码的时候,由于对 mixin 不熟悉导致理解出现偏差,走了很多弯路,所以这篇文章介绍一下 mixin 概 ...

  4. 权限管理整合springsecurity代码执行过程

    (1)输入用户名密码. (2)进入认证过滤器中,执行attemptAuthentication方法, 通过该方法获取输入的用户名和密码. (3)执行实现了UserDetailsService接口的类中 ...

  5. HDFS中的NameNode名节点——FSimage

    HDFS缓冲区 Fsimage 文件映射,Edits文件操作记录. 与ES的缓冲区不同,ES是维护数据的变更,而HDFS缓冲区是用于名结点维护文件系统元数据(目录树)的机制. 在HDFS集群中,Nam ...

  6. python进阶(2)python最常用的模块

    math math.ceil(a): 用来返回≥a的最小整数 math.floor(a):用来返回≤a的最大整数 round(a [,b]) 如果没有参数b,只有a,round()作用是四舍五入 如果 ...

  7. java对象克隆复制

    原文链接:https://blog.csdn.net/ztchun/article/details/79110096 自己先简单描述总结一下:当想要将一个对象中已有的值直接给另外一个对象的时候,其实并 ...

  8. TERSUS无代码开发(笔记07)-简单实例手机端后台逻辑开发

    提交申请逻辑开发 1.添加父级对象引用(从父级对象中获取前端输入框的值) 1.设计数据库表(表名和字段名称不能用中文) 2.设计置数据库主键(可设联合主键) 3.传值形成数据实列处理 4.服务器端处理 ...

  9. HTTP 请求URL中不能含有空格

    如果含有空格 会报 不合法参数异常 正确做法是将其encode URLEncoder.encode(targetString, "utf-8").replaceAll(" ...

  10. (数据科学学习手札109)Python+Dash快速web应用开发——静态部件篇(中)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...