C++单重继承分析
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类的虚表- class CBase
基类中有虚函数,派生类中无新增虚函数,派生类中重写了父类的虚函数
在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类虚表- class CDerived:public 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 CDerived:public CBase
虚析构
代码如下:
- 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++单重继承分析的更多相关文章
- ActiveRecord-连接多张表之单表继承
ActiveRecord-连接多张表之单表继承 1. 基本概念 Rails提供了两种机制,可以将复杂的面向对象模型映射为关系模型,即所谓的单表继承(single-table inheritance)和 ...
- SpringData-Redis发布订阅自动重连分析
SpringData-Redis发布订阅自动重连分析 RedisMessageListenerContainer 配置 @Bean @Autowired RedisMessageListenerCon ...
- python flask_sqlalchemy 多态 polymorphic 实现单表继承
sqlalchemy 多态 polymorphic 实现单表继承 sqlaclchemy中的单表继续就是以一个模型类为基类,其他模型类继承基类,所有模型类的的数据都存一张表里面(也可以是多张,只不过基 ...
- 【转】NG:垂枝桦基因组图谱构建(2+3组装)及重测序分析
转自希望组公众号.学习二代+三代组装策略的流程 垂枝桦(Betula pendula)是一种速生乔木,能在短短一年时间内开花,木质坚实,可做细工.家具等,经济价值极高.近日,芬兰研究人员对垂枝桦自交系 ...
- caffe可重入单例机制分析
一个函数可重入是指该函数可以被多个线程同时调用.大多数函数都不是可重如的,因为很多函数会修改静态数据结构里的内容,如果多个线程同时调用,势必破坏共享的静态结构.可以在不改变公共接口的情况下,将一个非重 ...
- C++继承分析
面向对象的三大特性之一就是继承,继承运行我么重用基类中已经存在的内容,这样就简化了代码的编写工作.继承中有三种继承方式即:public protected private,这三种方式规定了不同的访问权 ...
- MD4C/CO46/MD04一个很棒的工单缺料分析
大家好~~~ 之前在MD04物料分析的时候,看到有订单预留,双击有个订单报告可以显示一个订单物料是否缺料清单 这个单独的工单分析可以在T-code:MD4C,CO46查看,如果只是单独的使用,那么这两 ...
- java继承分析
把java学完之后有開始了一遍突然发现对于继承还是不太理解所以就做了一个測试来分析一下 <span style="font-size:18px;">class A{ p ...
- C++_代码重用4-多重继承
继承使用时要注意,默认是私有派生.所以要公有派生时必须记得加关键字Public. MI(Multi Inheritance)会带来哪些问题?以及如何解决它们? 两个主要问题: 从两个不同的基类继承同名 ...
随机推荐
- 数理统计11:区间估计,t分布,F分布
在之前的十篇文章中,我们用了九篇文章的篇幅讨论了点估计的相关知识,现在来稍作回顾. 首先,我们讨论了正态分布两个参数--均值.方差的点估计,给出了它们的分布信息,并指出它们是相互独立的:然后,我们讨论 ...
- 2021-2-16:请问你知道分布式设计模式中的Quorum思想么?
有效个数(Quorum) 有效个数(Quorum)这个设计模式一般是指分布式系统的每一次修改都要在大多数实例上通过来确定修改通过. 问题背景 在一个分布式存储系统中,用户请求会发到一个实例上.通常在一 ...
- Python+OpenCV+图片旋转并用原底色填充新四角
import cv2 from math import fabs, sin, cos, radians import numpy as np from scipy.stats import mode ...
- bash variables plus operator All In One
bash variables plus operator All In One Errors missing pass params #!/usr/bin/env bash # echo emoji ...
- Linked List & List Node All In One
Linked List & List Node All In One 链表 & 节点 链表类型 单链表 双链表 环形链表 / 循环链表 Singly Linked List (Uni- ...
- 使用 js 实现十大排序算法: 堆排序
使用 js 实现十大排序算法: 堆排序 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列: 小顶堆:每个 ...
- node.js & read argv
node.js & read argv https://nodejs.org/docs/latest/api/process.html https://flaviocopes.com/node ...
- TypeScript 3.7 RC & Nullish Coalescing
TypeScript 3.7 RC & Nullish Coalescing null, undefined default value https://devblogs.microsoft. ...
- Elasticsearch---DSL搜索实践
Domain Specific Language 特定领域语言,基于JSON格式的数据查询,查询更灵活,有利于复杂查询 一.普通url路径参数搜索 数据准备 1.建立名字为 shop 的索引 2.手动 ...
- 在 TKE 中使用 Velero 迁移复制集群资源
概述 Velero(以前称为Heptio Ark)是一个开源工具,可以安全地备份和还原,执行灾难恢复以及迁移 Kubernetes 群集资源和持久卷,可以在 TKE 集群或自建 Kubernetes ...