CSDN上已经有很多关于C++多态方面的一些系统介绍了,但是我看了一下一些有关于多态问题的细节问题文章较少,因此我想要出一片文章重点讲一讲我认为比较重点且容易被遗忘的知识点,一些比较基本的知识这里就不过多赘述了,可以参考其他优质博主的文章。
锁死多态的两个必要条件:
实现多态必须要实现虚函数的重写和指针or引用调用,这是人尽皆知的知识,但是在一些复杂一点考试题中,可能会把人给绕晕。
这一道考试题:
class A
{
public:
virtual void func(int val = 1){ std::cout<<“A->”<< val <<std::endl;}
virtual void test(){ func();}
};
class B : public A
{
public:
void func(int val=0){ std::cout<<“B->”<< val <<std::endl; }
};
int main(int argc ,char* argv[])
{
B*p = new B;
p->test();
return 0;
}
试着写一下运行结果是什么?你可能会写出B->0,当然也不排除别的情况,但是正确答案是B->1
具体原因见下图:
一定要牢记普通函数的继承是一种实现继承,而虚函数的继承是一种接口继承,{}以外的即函数接口,{}以内的即函数实现
改动一下题目,变成这样:
class A
{
public:
virtual void func(int val = 1){ std::cout<<“A->”<< val <<std::endl;}
};
class B : public A
{
public:
void func(int val=0){ std::cout<<“B->”<< val <<std::endl; }
virtual void test(){ func();}
};
int main(int argc ,char* argv[])
{
B*p = new B;
p->test();
return 0;
}
这样输出的结果就是B->0呢,此时test()已经不再是父类的成员函数,而是独属于子类,this指针自然变为B*类型,不满足多态的条件。
(这是一道笔试题,非常考察对多态条件判断的基本功)
不能virtual修饰的成员:
内联函数:
虚函数需要将函数地址存入虚表,而内联函数直接在调用出展开,是不存在地址的,也就没有对应能存入在虚表中指针变量。(但是部分编译器可能将内联函数识别为普通函数,使得virtual修饰不会报错)
(拷贝)构造函数:
指向虚表的指针在初始化列表中初始化的,无法在虚表指针未初始化之前在表中存放元素
静态成员函数:
.静态成员函数与具体对象无关,属于整个类,核心关键是没有隐藏的this指针,可以通过类名::成员函数名 直接调用,此时没有this无法拿到虚表,就无法实现多态,因此不能设置为虚函数
不要混淆虚表和虚基表:
虚基表是为了解决菱形继承造成的二义性问题,存放指针的偏移量(了解)
虚表是一个虚函数指针数组
虚表的一些细节:
1、父类和子类的虚表没有任何关系,二者虚表的地址不同,只不过在内容上可能相同(没有进行重写的时候)
/
/
/
/
/
/
2、地位均等的对象公用一张虚表
——————————————————————
再来看一道题目:
class A{
public:
A ():m_iVal(0){test();}
virtual void func() { std::cout<<m_iVal<<‘ ’;}
void test(){func();}
public:
int m_iVal;
};
class B : public A{
public:
B(){test();}
virtual void func(){
++m_iVal;
std::cout<<m_iVal<<‘ ’;}
};
int main(int argc ,char* argv[]){
A*p = new B;
p->test();
return 0;}
分析一下过程:
new B调用B的构造函数,会先调用父类的构造函数,此时虚表指针还未初始化,没有多态机制,仅仅普通调用A的构造函数,输出0;
而后再调用B构造函数的实现,再次调用test函数,此时虚表指针已经初始化,构成多态,因此输出1,同理输出2.