C++是如何利用虚函数实现多态性的?
2012-03-28 23:04
316 查看
前一篇:http://patmusing.blog.163.com/blog/static/1358349602010419111919799/
还是先看示例程序,用代码说话:
#include <iostream>
using namespace std;
class A
{
public:
inline virtual void vfun()
{
cout << "this is vfun in class A" << endl;
}
};
class B : public A
{
public:
inline void vfun()
{
cout << "this is vfun in class B" << endl;
}
};
int main(void)
{
A* p;
A P;
// 由于A中声明了虚函数,那么在定义对象P时,P中将包含A类的vfptr,
// 该vfptr指向A类的虚函数表,
p = &P;
// 因此下面的语句向显示”this
is vfun in class A”
p->vfun();
B* q;
// 由于A中声明了虚函数,类B继承了A,那么在定义对象Q时,Q中将包含B
// 类的vfptr,该vfptr指向B类的虚函数表
B Q;
q = &Q;
p = q;
// p 现在是类B对象的一个指针,即Q的地址,而Q中包含了指向B类虚函数
// 表的vfptr指针。因此下面的语句将显示
// “this is vfun in class B”
p->vfun();
return 0;
}
当然在实际编程中,没有人会象上面那样写程序的。运行结果:
再来一个虚函数和多态性例子:
#include <iostream>
using namespace std;
class Parent
{
public:
int parent;
public:
inline Parent()
{
}
inline Parent(int parent)
{
this->parent = parent;
}
inline virtual void haveFun()
{
cout << "haveFun in Parent" << endl;
cout << "parent = " << parent << endl;
}
};
class Child : public Parent
{
public:
int child;
public:
inline Child()
{
}
inline Child(int child) : Parent(child + 1)
{
this->child = child;
}
inline void haveFun()
{
cout << "haveFun in Child" << endl;
cout << "parent = " << parent << endl;
cout << "child = " << child << endl;
}
};
int main(void)
{
Parent* p = new Child(2);
p->haveFun();
return 0;
}
输出结果:
这正是我们希望看到的现象。但是如果我们把Parent类中haveFun函数前面的virtual去掉,结果会是什么样子呢?
多态性不见了,因此可以推论,没有虚函数就没有多态性。究竟背后是什么原因造成的呢?
我们知道如果一个类声明了虚函数,那么编译器就会在其声明的对象中的开始部分(至少对于VC而言是这样的)增加一个vfptr,vfptr指向虚函数表。如果没有声明虚函数,那么这vfptr就不存在。Parent*
p是声明了一个指向Parent对象的指针,Parent没有声明虚函数的情况下,p所指向的内容并不包括vfptr,当用new
Child(2)给它赋值时,除了将Child中的Parent
suboject以memberwise的方式拷贝给p所指向的Parent对象外,什么也没有做。因此p->haveFun()调用的是 Parent中的haveFun函数;
如果在Parent中haveFun是虚函数,那么当Parent*
p = new Child(2);执行时,就会把Child对象的vfptr和Child对象中包含的Parent
subobject一同以memberwise的方式拷贝给p所指向的对象。此后,p所指向vfptr是Child对象的vfptr,因此再调用 p->havefun()时,就会调用Child类中的haveFun函数。另外值得一提的是,如果不进行explicit形式的类型转换p所指的对象只有vfptr(由vfptr得到虚函数haveFun)和Parent::parent,在VC中的表现如下:
既然我们对p的初始化是Parent*
p = new Child(2),即是通过一个Child对象的指针来初始化的,为什么p中看不到Child中的child成员变量呢?原因是Parent*
p所隐含的意思是,p指向一个Parent对象,包括vfptr在内共8
bytes,Child*所隐含的意思是,它所指向的是一个Child对象,包括vfptr在内共12
bytes,如下图:
当Parent* p = new Child(2)执行时,会发生如下图所示的过程:
也就是说,p所指向的内存块有vfptr,parent以及child,只是由于 Parent的大小为8
bytes,因此限制了p缺省地只能看到8
bytes,如果用Child*对p进行指针类型的转换,就可以看到12
bytes,也就是说可以看到Child的成员变量child了,如下:
还是先看示例程序,用代码说话:
#include <iostream>
using namespace std;
class A
{
public:
inline virtual void vfun()
{
cout << "this is vfun in class A" << endl;
}
};
class B : public A
{
public:
inline void vfun()
{
cout << "this is vfun in class B" << endl;
}
};
int main(void)
{
A* p;
A P;
// 由于A中声明了虚函数,那么在定义对象P时,P中将包含A类的vfptr,
// 该vfptr指向A类的虚函数表,
p = &P;
// 因此下面的语句向显示”this
is vfun in class A”
p->vfun();
B* q;
// 由于A中声明了虚函数,类B继承了A,那么在定义对象Q时,Q中将包含B
// 类的vfptr,该vfptr指向B类的虚函数表
B Q;
q = &Q;
p = q;
// p 现在是类B对象的一个指针,即Q的地址,而Q中包含了指向B类虚函数
// 表的vfptr指针。因此下面的语句将显示
// “this is vfun in class B”
p->vfun();
return 0;
}
当然在实际编程中,没有人会象上面那样写程序的。运行结果:
再来一个虚函数和多态性例子:
#include <iostream>
using namespace std;
class Parent
{
public:
int parent;
public:
inline Parent()
{
}
inline Parent(int parent)
{
this->parent = parent;
}
inline virtual void haveFun()
{
cout << "haveFun in Parent" << endl;
cout << "parent = " << parent << endl;
}
};
class Child : public Parent
{
public:
int child;
public:
inline Child()
{
}
inline Child(int child) : Parent(child + 1)
{
this->child = child;
}
inline void haveFun()
{
cout << "haveFun in Child" << endl;
cout << "parent = " << parent << endl;
cout << "child = " << child << endl;
}
};
int main(void)
{
Parent* p = new Child(2);
p->haveFun();
return 0;
}
输出结果:
这正是我们希望看到的现象。但是如果我们把Parent类中haveFun函数前面的virtual去掉,结果会是什么样子呢?
多态性不见了,因此可以推论,没有虚函数就没有多态性。究竟背后是什么原因造成的呢?
我们知道如果一个类声明了虚函数,那么编译器就会在其声明的对象中的开始部分(至少对于VC而言是这样的)增加一个vfptr,vfptr指向虚函数表。如果没有声明虚函数,那么这vfptr就不存在。Parent*
p是声明了一个指向Parent对象的指针,Parent没有声明虚函数的情况下,p所指向的内容并不包括vfptr,当用new
Child(2)给它赋值时,除了将Child中的Parent
suboject以memberwise的方式拷贝给p所指向的Parent对象外,什么也没有做。因此p->haveFun()调用的是 Parent中的haveFun函数;
如果在Parent中haveFun是虚函数,那么当Parent*
p = new Child(2);执行时,就会把Child对象的vfptr和Child对象中包含的Parent
subobject一同以memberwise的方式拷贝给p所指向的对象。此后,p所指向vfptr是Child对象的vfptr,因此再调用 p->havefun()时,就会调用Child类中的haveFun函数。另外值得一提的是,如果不进行explicit形式的类型转换p所指的对象只有vfptr(由vfptr得到虚函数haveFun)和Parent::parent,在VC中的表现如下:
既然我们对p的初始化是Parent*
p = new Child(2),即是通过一个Child对象的指针来初始化的,为什么p中看不到Child中的child成员变量呢?原因是Parent*
p所隐含的意思是,p指向一个Parent对象,包括vfptr在内共8
bytes,Child*所隐含的意思是,它所指向的是一个Child对象,包括vfptr在内共12
bytes,如下图:
当Parent* p = new Child(2)执行时,会发生如下图所示的过程:
也就是说,p所指向的内存块有vfptr,parent以及child,只是由于 Parent的大小为8
bytes,因此限制了p缺省地只能看到8
bytes,如果用Child*对p进行指针类型的转换,就可以看到12
bytes,也就是说可以看到Child的成员变量child了,如下:
相关文章推荐
- C++是如何利用虚函数实现多态性的?
- C++是如何利用虚函数实现多态性的?
- C++是如何利用虚函数实现多态性的?
- C++是如何利用虚函数实现多态性的?
- C++是如何利用虚函数实现多态性的?
- C++学习之路—多态性与虚函数(一)利用虚函数实现动态多态性
- 利用虚函数实现多态性
- 如何利用C/C++逐行读取txt文件中的字符串(可以顺便实现文本文件的复制)
- C++利用纯虚函数和虚函数实现接口继承和实现继承
- C++虚函数多态性的实现与分析+虚继承的实现与分析
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- 利用C++模板,代替虚函数,实现类的静态多态性
- 如何在C++中实现多态性
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- 请教:如何实现UDDI,利用C++或者C#?
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- C++中如何实现像Java中接口功能--C++抽象类(纯虚函数,虚函数)
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- 利用C++模板,代替虚函数,实现类的静态多态性(加入性能测试部分)
- 如何利用C/C++逐行读取txt文件中的字符串(可以顺便实现文本文件的复制)