虚函数,纯虚函数,虚拟继承问题:
C++虚继承可以防止多重继承产生的二义性问题。
虚继承,就是在被继承的类前面加上virtual关键字,这时被继承的类称为虚基类,如下面代码中的base类。虚继承在多重继承的时可以防止二义性
class base
class derived1 : virutal public base
class derived2 : virtual public base
class derived3 : public derived1, public derived2
以上的代码如果用到了base中的某个成员变量就不会产生二义性。和#progma once在头文件中的作用类似。请看下面的例子:
#include
using namespace std;
class Parent
{
public:
int p; // p将会被所有的子类继承,也将是二义性的根源
inline Parent()
{
p = 10;
}
};
class Child1 : public Parent
{
public:
int c1;
inline Child1()
{
p = 12; // p在子类Child1中被赋值为12
c1 = 12;
}
};
class Child2 : public Parent
{
public:
int c2;
inline Child2()
{
p = 13; // p在子类Child2中被赋值为13
c2 = 13;
}
};
class GrandChild : public Child1, public Child2
{
public:
int grandchild;
// p显然也存在于GrandChild中,但是到底是12,还是13呢?这就产生了二义性
inline GrandChild()
{
grandchild = 14;
}
};
int main(void)
{
GrandChild* pGC = new GrandChild();
cout << pGC->p << endl;
return 0;
}
上面程序是不能通过编译的,编译器输出的错误信息如下:
…: error C2385: 'GrandChild::p' is ambiguous
…: warning C4385: could be the 'p' in base 'Parent' of base 'Child1' of class 'GrandChild'
…: warning C4385: or the 'p' in base 'Parent' of base 'Child2' of class 'GrandChild'
正如编译器告诉我们的那样,GrandChild::p是模棱两可,它被Child1继承了即Child1中包含了一个Parent subobject,也被Child2继承了即Child2中也包含了一个Parent suboject,然后GrandChild又同时继承了Child1和Child2,根据“derived class中要保持base class的完整原样性原则”,因此GrandChild包含了两个ParentObject。所以当pGC->p时,编译器根本无法确定是调用Child1::p还是Child2::p,由此边产生了模棱两可的情形。怎么解决这样的问题呢?答案就是用虚继承或者叫虚基类的方式。
在上面的示例程序中做如下改动:
class Child1 : public Parent -> class Child1 : virtual public Parent
class Child2 : public Parent -> class Child2 : virtual public Parent
GrandChild的定义维持不变:
class GrandChild : public Child1, public Child2
做了上述改动后,即增加了两个virtual关键字,再编译就不会出现ambiguous之类的错误了。这是因为加上了virtual关键字后,就可以保证Parent suboject在GrandChild中只存在一份,从而消除了ambiguity。上面修改后的程序运行结果是13,这说明Child2类的那个p起了作用,如果GrandChild的定义写成:
class GrandChild : public Child2, public Child1
那么运行结果将是12。
上面的试验结果表面,在多重继承的时候,如果父类中有同名的成员变量(类似这篇文章中谈及的例子),为了防止二义性,一般要采用虚继承的方式,并且最右边的基类中的那个成员变量会出现在派生类对象中。
虚函数与纯虚函数的区别:
纯虚函数仅仅需要在基类里声明,不许要实现。实现是在派生类中进行实现。
子类中必须实现基类中的纯虚函数
带纯虚函数的类叫虚基类,不能直接定义对象,必须要在子类中定义后才能使用。
Virtual void eat() = 0;
在很多情况下基类本身生成对象是不合情理的。例如一个基类是人,派生出学生,老师,工人,农民,司机等等,这些派生类生成的对象是合理的,但是用基类生成一个对象确是不合里的。所以很多函数没有必要在基类里面完成,仅仅给予下层一个接口就可以了,让下层知道基类中需要这样一个函数。
虚函数是要在基类里实现的,即使是空实现。
子类中不必须实现基类中的虚函数。
作用仅仅是让派生类重载。
Virtual void eat(){};
虚函数规则:
1、在派生类中虚函数被重新定义时,函数原型必须基类中的完全相同。两者的函数体内容不同。
2、使用指向基类的指针访问虚函数是才能体现多态性。
3、虚函数仅仅适用于有继承关系的类对象,所以普通函数不能说明为虚函数。
4、虚函数不能为友元函数,不能为内敛函数,不能为静态成员函数,不能为构造函数。且一个虚函数无论被继承来多少次仍然保持其虚函数特性。
阅读(4780) | 评论(0) | 转发(0) |