注:虚函数和虚继承是两个概念,解决的问题也不同;
虚函数:防止多重派生时,使用指针调用同名函数时以基类函数为准(非同名隐藏规则)
//使用virtual来达到同名隐藏规则的效果 B b(1, 2, 3, 4); A0 *a; a = &b; a->Show(); //同名隐藏规则下的派生类函数直接输出自己的函数
虚继承(虚基类):解决“121”类二义性问题(防止在多重派生的基础上再次派生而产生二义性)
根据"赋值(父子)兼容规则":派生类可以作为父类使用,因为其继承了基类,但是基类不能够作为派生类使用;
在兼容规则下,使用派生类作为基类使用的时候,当遇到基类与派生类中存在同名的函数的时候,编译器会以以基类的函数为准,但是这不免有些使用不便;
为了产生:指定哪个类调用哪个类的函数的效果,于是产生了虚函数的概念;
1> 虚函数的理解
2> 虚函数的使用 (配合父子兼容规则)
3> 虚析构函数
4> 为什么要使用虚函数?
************************************************************************************************************************************
什么是虚函数?
某基类中声明为virtual并在一个或多个派生类中重新定义的成员函数叫做虚函数;
虚函数有什么作用?
虚函数的作用是实现动态联编,也就是在函数运行阶段动态的选择合适的成员函数;
在定义了虚函数后,可以在派生类对虚函数进行重写,从而实现统一的接口,不同的执行过程。
在派生类中重写虚函数的时候,要保持重写的函数与原函数的一致性(包括返回值类型、参数个数与类型);
虚函数在定义的时候,基类结构中增加了一个虚函数指针表virtual table, 该表存放虚函数的调用地址,大小由虚函数个数决定,该表的首地址由基类内部指针vPtr指向。
如果继承类中存在同名的函数,则替换虚函数指针表中的相应同名函数,从而达到输出本类中的同名函数的效果。
1> 首先举一个"父子兼容规则"的源码示例
#include
using namespace std;
class A
{
private:
public:
void Show() const
{
cout << "A0::Show" << endl;
}
};
class A1 :public A
{
private:
public:
void Show() const
{
cout << "A1::Show" << endl;
}
};
class A2 :public A
{
private:
public:
void Show() const
{
cout << "A2::Show" << endl;
}
};
void Func(A &a)
{
a.Show();
}
int main()
{
A a;
A1 a1;
A2 a2;
Func(a);
Func(a1);
Func(a2);
return 0;
}
运行结果很明显:
2> 接下来给出使用虚函数后的源码以及运行结果:
源码:
#include
using namespace std;
class A
{
private:
public:
virtual void Show() const
{
cout << "A0::Show" << endl;
}
};
class A1 :public A
{
private:
public:
void Show() const
{
cout << "A1::Show" << endl;
}
};
class A2 :public A
{
private:
public:
void Show() const
{
cout << "A2::Show" << endl;
}
};
void Func(A &a)
{
a.Show();
}
int main()
{
A a;
A1 a1;
A2 a2;
Func(a);
Func(a1);
Func(a2);
return 0;
}
<和上述代码相比仅仅加了一个virtual,当然也可以每个同名函数前都可以添加virtual函数,但是因为A1和A2是直接继承于A的,虚函数指针表 (Virtual Table)是可以被继承的,所以只要在基类中声明即可;>
<如果派生类中继续派生,则在派生类中和派生类的派生类中对派生类中的同名函数使用virtual即可>
运行结果:
* 当然也可以对供调用的全局函数传入指针作为形参:
* 上面的操作似乎已经完全ODK了,但是还有一个问题,就是"父子兼容规则"下析构韩函数的调用问题:
1> 未使用虚析构函数:
#include
using namespace std;
class A
{
public:
~A()
{
cout << "A's destruct function!" << endl;
}
};
class A1 :public A
{
public:
~A1()
{
cout << "A1's destruct function!" << endl;
}
};
void Func(A *a)
{
delete a;
}
int main()
{
A1 *a = new A1;
Func(a);
return 0;
}
运行结果:
2> 使用虚析构函数
#include
using namespace std;
class A
{
public:
virtual ~A()
{
cout << "A's destruct function!" << endl;
}
};
class A1 :public A
{
public:
~A1()
{
cout << "A1's destruct function!" << endl;
}
};
void Func(A *a)
{
delete a;
}
int main()
{
A1 *a = new A1;
Func(a);
return 0;
}
运行结果:
首先,如果想要显示派生类中的与基类中同名的函数,直接使用派生类实例化的对象直接调用不就可以了吗?
像这样:
但是,条条大路通罗马,不同的解决方法会适用于不同的场景:
虚函数向我们提供了一种不使用实例化的对象通过“X.x()”的形式来显示函数的方法(虚函数是通过直接把实例化的对象作为参数传入一个单独的<公有显示函数>进行显示)。