多继承中重写不同基类中的虚函数
来源:互联网 发布: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(); // 调用类CDerived的TestA()函数
pB->TestB(); // 调用类CDerived的TestB()函数
}
——————————————————————————————
可是,如果两个基类中有一个相同原型的虚函数,例如下面这样:
例二:
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行代码都将调用类CDerived的Test()函数
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(); // 这里实际上是重写CBaseA的Test()
virtual void CBaseB_Test(); // 这里实际上是重写CBaseB的Test()
};
void Test()
{
CDerived D;
CBaseA *pA = &D;
CBaseB *pB = &D;
// 调用类CBaseA的Test()函数
// 由于C++多态的特性,实际上调用的是类CDervied中的CBaseA_Test()函数
pA->Test();
// 调用类CBaseB的Test()函数
// 由于C++多态的特性,实际上调用的是类CDervied中的CBaseB_Test()函数
pB->Test();
}
现在以上面代码中的pA->Test();这行代码来说明上面的方案是怎么实现的。
首先,由于虚函数Test()在类CBaseA的派生类CMiddleBaseA中被重写,所以这行代
码会去调用类CMiddleBaseA的Test()函数;
然后,类CMiddleBaseA的Test()函数会去调用实现函数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
在编译时期,因为p的typeid是GrandParent*,所以在使用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的类型在动态解析时得到的是自身的类型。
- 多继承中重写不同基类中的虚函数
- C++多继承中重写不同基类中相同原型的虚函数
- C++多继承中重写不同基类中相同原型的虚函数
- C++多继承中重写不同基类中相同原型的虚函数
- C++多继承中重写不同基类中相同原型的虚函数
- C++多重继承通过中间类继承重写不同基类中相同类型的虚函数
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- C++类的继承关系——多继承(未重写虚函数)
- Java中多层继承中的方法重写
- 【C++】虚函数在不同继承方式中的对象模型
- C++多重继承中重写不同基类中相同…
- 多继承中的虚函数
- 多继承中的虚函数
- 谈谈c++中继承中的虚函数
- java 继承中的重写父类方法
- 继承中的方法重写
- 继承中的方法重写
- 成员函数的重载、覆盖与隐藏
- java + soap 环境配置
- 嵌入式和单片机的区别
- 工作回忆总结(第一年)
- 基于DeepZoom技术的Bing Maps客户端实现研究
- 多继承中重写不同基类中的虚函数
- JavaScript——悟透JavaScript
- DM644X启动过程浅析
- SQL Server 2008表设计器中无法保存修改
- ems 快递查询
- 3.6如何编程来关闭一个打开文档的所有视图?
- OD Commands
- 堆和栈的区别
- Javascript——闭包