初识虚函数
虚函数
源码如下
#include <iostream>
using namespace std;
class A{
public:
virtual void printAA(){cout<<"print A";}
};
class B:public A{
public:
virtual void printAA(){cout<<"print B";}
};
int main()
{
A* a = new B();
a->printAA();
B b;
b.printAA();
delete a;
return 0;
}
编译器生成如下
#include <iostream>
using namespace std;
class A
{
public:
inline virtual void printAA()
{
std::operator<<(std::cout, "print A");
}
// inline constexpr A() noexcept = default;
};
class B : public A
{
public:
inline virtual void printAA()
{
std::operator<<(std::cout, "print B");
}
// inline constexpr B() noexcept = default;
};
int main()
{
A * a = static_cast<A *>(new B());
a->printAA();
B b = B();
b.printAA();
delete a;
return 0;
}
汇编代码如下:
.LC0:
.string "print A"
A::printAA():
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
.LC1:
.string "print B"
B::printAA():
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
A::A() [base object constructor]:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov edx, OFFSET FLAT:vtable for A+16
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax], rdx
nop
pop rbp
ret
B::B() [base object constructor]:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov rdi, rax
call A::A() [base object constructor]
mov edx, OFFSET FLAT:vtable for B+16
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax], rdx
nop
leave
ret
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 24
mov edi, 8
call operator new(unsigned long)
mov rbx, rax
mov QWORD PTR [rbx], 0
mov rdi, rbx
call B::B() [complete object constructor]
mov QWORD PTR [rbp-24], rbx
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax]
mov rdx, QWORD PTR [rax]
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call rdx
mov eax, OFFSET FLAT:vtable for B+16
mov QWORD PTR [rbp-32], rax
lea rax, [rbp-32]
mov rdi, rax
call B::printAA()
mov rax, QWORD PTR [rbp-24]
test rax, rax
je .L6
mov rdi, rax
call operator delete(void*)
.L6:
mov eax, 0
mov rbx, QWORD PTR [rbp-8]
leave
ret
vtable for B:
.quad 0
.quad typeinfo for B
.quad B::printAA()
vtable for A:
.quad 0
.quad typeinfo for A
.quad A::printAA()
typeinfo for B:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for B
.quad typeinfo for A
typeinfo name for B:
.string "1B"
typeinfo for A:
.quad vtable for __cxxabiv1::__class_type_info+16
.quad typeinfo name for A
typeinfo name for A:
.string "1A"
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L10
cmp DWORD PTR [rbp-8], 65535
jne .L10
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
.L10:
nop
leave
ret
_GLOBAL__sub_I_main:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
运行结果,可以保证在运行期实现动态绑定。
print Bprint B
非虚函数
若类A和类B 的printAA()函数均不是虚函数,生成如下,从代码可以看出是没有变化的,只是无法在运行期无法实现动态绑定了而已。
#include <iostream>
using namespace std;
class A
{
public:
inline void printAA()
{
std::operator<<(std::cout, "print A");
}
// inline constexpr A() noexcept = default;
};
class B : public A
{
public:
inline void printAA()
{
std::operator<<(std::cout, "print B");
}
// inline constexpr B() noexcept = default;
};
int main()
{
A * a = static_cast<A *>(new B());
a->printAA();
B b = B();
b.printAA();
delete a;
return 0;
}
汇编代码如下:
.LC0:
.string "print A"
A::printAA():
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
.LC1:
.string "print B"
B::printAA():
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
mov esi, OFFSET FLAT:.LC1
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
nop
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, 1
call operator new(unsigned long)
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov rdi, rax
call A::printAA()
lea rax, [rbp-9]
mov rdi, rax
call B::printAA()
mov rax, QWORD PTR [rbp-8]
test rax, rax
je .L4
mov rdi, rax
call operator delete(void*)
.L4:
mov eax, 0
leave
ret
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L8
cmp DWORD PTR [rbp-8], 65535
jne .L8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
.L8:
nop
leave
ret
_GLOBAL__sub_I_main:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
运行结果如下:
print Aprint B
非虚函数可能出现的问题
- 用类B去实例化一个类A对象,那么printAA永远用的是类A的printAA。
- 类C 同时继承类A和类B,那么会导致菱形问题。