在我们学习c++的过程中我们经常会遇到这样一个词语———多态性
那么,什么是多态性呢?
1.多态就是一个接口的多种实现
2.允许将子类指针赋予父类指针,以让父类可以执行一个或者多个子类的特有功能.
classA
{
public
:
A(){}
void
foo()
{
cout<<
"ThisisA."
<<endl;
}
};
classB:publicA
{
public
:
B(){}
void
foo()
{
cout<<
"ThisisB."
<<endl;
}
};
int
main(intargc,
char
*argv[])
{
A* a =
new
B();
a->foo();
if
(a != NULL)
delete
a;
return0;
}
输出:ThisisA.
如上所示 我们知道类B继承与类A 所以A* a =
new
B();这句代码是可以编译通过的
我们看看B类对象的内存布局吧:
在这里我们可以看到 B的总内存中既有自身的内存 也有继承于A的内存空间
那么A* a =
new
B()这句代码就可以理解为 a指向了B中继承于A的那一块内存空间
因此在执行a->foo();这一句代码的时候 就找到了A中的foo函数 也就得到最后的结果:ThisisA.
以上程序 并未体现多态,下面我们有请多态登场!
classA
{
public
:
A(){}
virtual
void
foo() //
我们在这里加入了一个关于多态的关键字 virtual
{
cout<<
"ThisisA."
<<endl;
}
};
classB:publicA
{
public
:
B(){}
void
foo()
{
cout<<
"ThisisB."
<<endl;
}
};
int
main(intargc,
char
*argv[])
{
A* a =
new
B();
a->foo();
if
(a != NULL)
delete
a;
return0;
}
是的 正如你所见 在我们加上virtual这个奇怪的关键字后 我们的这句代码 a->foo();调用了属于B内存空间的foo (一个父类的指针,被子类对象赋值后,竟然调用了子类的方法!!!!)没错!这就是丧心病狂的多态!
为什么会这样捏?
当我们的程序在编译的时候 在碰到普通函数(不带virtual关键字的函数)的时候呢,此时就确定了该调用哪个函数,这叫做先期联编,而在碰到了virtual的时候,就会告诉编译器,我们在运行的时候在来确定这个函数的调用者吧!这就叫做滞后联编!
那么编译器是怎样实现滞后联编的呢?
我们的编译器在编译的时候为每一个包含了virtual函数(虚函数)的类都生成了一张虚函数表,为每一个这种含有虚函数的类的对象都生成了一个虚指针,指针就指向这个虚表,通过这个虚指针指向的虚表就可以在运行时动态的知道该调用什么函数!
这里说几个个我曾面试遇到的问题:
1.虚表和虚指针是什么时候生成的呢?
这个问题我已经解答了: 编译时期
2.虚表存在于内存的什么地方呢?
如果我们不知道这个答案的话,就会想当然的想,虚表肯定时存在于栈区罗.答案是错误的,经过我上机实验,我发现虚表是存在于 内存常量区的 对于这个结果我的想法是:虚表是一张关于虚函数内存地址的记录,在编译时期就已经生成,为了程序的健康运行,肯定是不想要有什么情况去修改我们的虚表的,所有就将虚表存放在了常量内存区
3.构造函数和析构函数能否为虚函数呢?
1.构造函数:我们知道虚函数是依赖于虚表进行调用的 在构造函数构造之前连虚表的没有 如何去调用虚函数呢 所以 构造函数不可以为虚函数
2.析构函数:我们提倡吧析构函数设为虚函数 因为经常会有继承的现象出现 在我们将析构函数设置为虚函数后 可以更好的按类型执行虚函数
4.以下程序的输出?
class MyClass
{
public:
MyClass(){};
~MyClass(){};
};
class VitualClass
{
public:
VitualClass(){};
virtual~VitualClass(){};
private:
};
void main()
{
//不含虚表的类 和 不含虚指针的对象
MyClass a;
cout<<sizeof(MyClass)<<endl;
cout<<sizeof(a)<<endl;
//含虚表的类 和 含虚指针的对象
VitualClass b;
cout<<sizeof(VitualClass)<<endl;
cout<<sizeof(b)<<endl;
}
运行结果:
1
1
4
4
ps:于32位平台上 一个指针占4个字节
好了,以上就是我关于多态的一点认识,有不足的地方希望多多指正哦!