多继承中重写不同基类中的虚函数

来源:互联网 发布:object数组怎么调用 编辑:程序博客网 时间:2024/05/21 06:27

多继承中重写不同基类中的虚函数

C++多继承体系当中,在派生类中可以重写不同基类中的虚函数。

下面就是一个例子:

——————————————————————————————

例一:

class CBaseA

{

public:

     virtual void TestA();

};

class CBaseB

{

public:

     virtual void TestB();

};

class CDerived : public CBaseA, public CBaseB

{

public:

     virtual void TestA(); // 重写基类CBaseA中的虚函数TestA()

     virtual void TestB(); // 重写基类CBaseB中的虚函数TestB()

};

void Test()

{

     CDerived D;

     CBaseA *pA = &D;

     CBaseB *pB = &D;

     pA->TestA(); // 调用类CDerivedTestA()函数

     pB->TestB(); // 调用类CDerivedTestB()函数

}

——————————————————————————————

可是,如果两个基类中有一个相同原型的虚函数,例如下面这样:

例二:

class CBaseA

{

public:

     virtual void Test();

};

class CBaseB

{

public:

     virtual void Test();

};

     怎样在派生类中重写这两个相同原型的虚函数呢?

     也许这种情况并不常见,可是这种情况却确实存在。比如说开发的时候使用的两个

类库是不同的厂商提供的,或者说这两个类库是由公司

的不同开发小组开发的。对前者来说,修改基类的接口是不可能的;对后者来说,修改

接口的代价很大。

     如果在派生类中直接重写这个虚函数,那么2个基类的Test()虚函数都将被覆盖。这

样的话就只能有一个Test()的实现,而不是像前面的例

子那样有不同的实现。

class CDerived : public CBaseA, public CBaseB

{

public:

     virtual void Test();

};

void Test()

{

     CDerived D;

     CBaseA *pA = &D;

     CBaseB *pB = &D;

     // 下面2行代码都将调用类CDerivedTest()函数

     pA->Test();

     pB->Test(); 

}

——————————————————————————————

     为了实现第一个例子中的那样,在派生类CDerived中重写不同基类中相同原型的虚

函数Test(),可以使用下面的方法。

     首先,不需要对2个基类进行任何修改(在实际的开发当中,修改基类的可能性非常小)

例三:

class CBaseA

{

public:

     virtual void Test();

};

class CBaseB

{

public:

     virtual void Test();

};

     现在,为这个继承体系添加2个中间类,分别从2个基类派生。

class CMiddleBaseA : public CBaseA

{

private:

     // 真正的实现函数

     // 设置为纯虚函数,在派生类里必须实现

     virtual void CBaseA_Test() = 0;

     // 改写继承下来的虚函数

     // 仅仅直接调用真正的实现函数

     virtual void Test() 

     { 

         CBaseA_Test(); 

     }

};

// 与类CMiddleBaseA采用相同的方法

class CMiddleBaseB : public CBaseB

{

private:

     virtual void CBaseB_Test() = 0;

     virtual void Test() 

     { 

         CBaseB_Test(); 

     }

};

     然后,类CDerived以上面2个中间类作为基类来派生。分别重写上面2个基类中原型

不同的纯虚函数,添加不同的实现代码。

class CDerived : public CMiddleBaseA, public CMiddleBaseB

{

private:

     // 重写从中间类继承下来的虚函数

     virtual void CBaseA_Test(); // 这里实际上是重写CBaseATest()

     virtual void CBaseB_Test(); // 这里实际上是重写CBaseBTest()

};

void Test()

{

     CDerived D;

     CBaseA *pA = &D;

     CBaseB *pB = &D;

     // 调用类CBaseATest()函数

     // 由于C++多态的特性,实际上调用的是类CDervied中的CBaseA_Test()函数

     pA->Test(); 

     // 调用类CBaseBTest()函数

     // 由于C++多态的特性,实际上调用的是类CDervied中的CBaseB_Test()函数

     pB->Test();

}

     现在以上面代码中的pA->Test();这行代码来说明上面的方案是怎么实现的。

     首先,由于虚函数Test()在类CBaseA的派生类CMiddleBaseA中被重写,所以这行代

码会去调用类CMiddleBaseATest()函数;

     然后,类CMiddleBaseATest()函数会去调用实现函数CBaseA_Test()

     最后,由于虚函数CBaseA_Test()在类CMiddleBaseA的派生类CDerived中被重写,所

以真正调用的是类CDerived中的CBaseA_Test()函数。

     同样的道理,代码pB->Test();实际上调用的是类CDervied中的CBaseB_Test()函数。

     通过上面的方法就可以在C++多继承中重写不同基类中相同原型的虚函数。

另附一份解释:

这是一个很好的问题!

我曾经轻视过它,但是现在我意识到这个问题其实很简单,但是有些subtle

看以下的代码:

class GrandParent

{

public:

virtual void kick()

{

// kick the Parent

}

};

class Parent

{

private:

virtual void kick()

{

// kick the children

}

};

class Child

{

protected:

virtual void kick()

{

// kick the dog

}

};

void test(void)

{

     GrandParent* p=new Child;

}

在这里p->kick的绝对是dog。问题出在这里:在public, protected, private等等权限只存

在于编译时期,而不在运行时起。多态性则发生在运行时期而不在编译时期。这样,容易看

出,虚表的结构大致如下:

child structure:

vptr of Child

vptr of Parent

vptr of GrandParent

vtbl of Child:

ptr of Child::kick             // NO PUBLIC/PROTECT/PRIVATE ACCESS INFORMATION IN

VTBL!

vtbl of Parent:

ptr of Parent::kick

vtbl of GrandParent

ptr of GrandParent::kick

在编译时期,因为ptypeidGrandParent*,所以在使用p->kick时编译器检查

GrandParent中相应的内容,因为p指针指向的是Children类产生的对象中的GrandParent

分,发现为public。于是编译通过。这是一个静态的过程。

p->kick的代码在编译之后,因为运行时是动态识别对象类型的,这样因为p指向的具体数据

Child类型的,所以在kick时传递的this指针的类型是Child*的,虚表结构中因为对象的

类型是Child,这样Child中的成员优先调用(此时没有访问权限检查!!),然后就访问了

Child*->kick

这就是这个问题的解决之道。其实我在看这个问题的时候,首先想到的是

class Derive

{

public:

virtual void test()

{

if (typeid(this)==typeid(ParentA*)) ...

if (typeid(this)==typeid(ParentB*)) ...

}

};

但是没有用。因为this的类型在动态解析时得到的是自身的类型。

 

原创粉丝点击