一个问题的讨论
今天在QQ里跟大家讨论了一个问题,虽然没有结论,但是还是贴出来留个记号。
崔刚(崔刚) 2007-10-25 16:18:55
如何通过基类指针调用派生类特有方法,前提是不允许强制转换指针类型?
例如有基类 class base{};
派生类
class derive:public base{
public:
int A();
}
我得到了一个base* 类型的指针 derive_ptr,但它指向derive类型对象,我如何调用方法 derive::A(), 不可以
((derive*)derive_ptr)->A();
李奇兵(李奇兵) 2007-10-25 16:20:17
把方法A()申明为虚的
陶华(陶华) 2007-10-25 16:20:52
除非基类有这个虚接口
李奇兵(李奇兵) 2007-10-25 16:20:55
把方法A()申明为虚的,在Base定义一个。
向官文(向官文) 2007-10-25 16:21:16
用函数指针应该可以实现吧
陈道生(陈道生) 2007-10-25 16:22:46
没方法
崔刚(崔刚) 2007-10-25 16:22:27
呵呵,如果base类有几十个派生类,每个派生类都有不同的特有方法,那么在基类中就会存在几十个虚方法,而每个派生类只实现一个,其它全部不实现,
这样是不是不太好??
李伟(监护)(李伟(监护)) 2007-10-25 16:23:25
如果是public的,基类应该声明吧
张建生(张建生) 2007-10-25 16:23:57
这样的话该函术确实不应该在基类里面定义
基类里面只定义共有的
靳波(靳波) 2007-10-25 16:23:47
这个问题的答案肯定很有技巧,但我认为这种技巧太花哨,不知道也没关系
陶华(陶华) 2007-10-25 16:24:49
崔刚,你那样定义是违反替换原则的
向官文(向官文) 2007-10-25 16:25:02
咱们的消息映射机制就是用基类的函数指针调派生类的成员函数
李伟(监护)(李伟(监护)) 2007-10-25 16:25:02
还是想知道。。
莫书健(莫书健) 2007-10-25 16:25:22
上面那段代码会不会运行是对的?
只是这样强制转换不是很符合编码规范
崔刚(崔刚) 2007-10-25 16:25:28
呵呵,不要怀疑我的问题的实用性
彭建军(彭建军) 2007-10-25 16:26:34
对于这个问题,C++的标准解决方法应该是dynamic_cast
靳波(靳波) 2007-10-25 16:26:15
cast也是类型转换
莫书健(莫书健) 2007-10-25 16:27:41
加不加dynamic_cast之类的,运行效果应该一样
彭建军(彭建军) 2007-10-25 16:29:18
是类型转换,但那是C++标准的转换方式。
向官文(向官文) 2007-10-25 16:30:45
dynamic_cast 据说会损失执行期效率
靳波(靳波) 2007-10-25 16:31:33
dynamic是在运行时进行转换,所以不是据说,是肯定会损失执行期效率。
靳波(靳波) 2007-10-25 16:36:22
#include "stdafx.h"
#include <iostream>
using namespace std;
class Base
{
};
class Derive : public Base
{
public:
int A(){cout<< "Derive::A()\n"; return 0;}
};
union Amazing
{
Base* pBase;
Derive* pDerive;
};
int _tmain(int argc, _TCHAR* argv[])
{
Base* derive_prt = new Derive;
Amazing trick;
trick.pBase = derive_prt;
trick.pDerive->A();
delete derive_prt;
return 0;
}
向官文(向官文) 2007-10-25 16:37:45
typedef void (base:: *BasefnPtr) ();
BasefnPtr fn_ptr = &derive::A;
然后用基类的指针去调
(pBase->*fn_ptr)();
我看高端里消息响应函数就这样调
没理解错吧 ;)
崔刚(崔刚) 2007-10-25 16:39:09
同志们,跑题了
1、不能强制转换,标准的强制转换也不行。
2、不是把派生类指针转成基类指针,那样是没问题的。我说的是把基类指针转成派生类指针。
杨晓涛(杨晓涛) 2007-10-25 16:40:14
靳波的方法挺有创意的,呵呵
靳波(靳波) 2007-10-25 16:40:45
:)
莫书健(莫书健) 2007-10-25 16:41:37
jinbo的方法有什么优点?
崔刚(崔刚) 2007-10-25 16:41:46
靳波太搞笑了,这样写不可维护
靳波(靳波) 2007-10-25 16:41:41
这个方法太危险
崔刚(崔刚) 2007-10-25 16:42:13
变相的强制转换,不合格,0分
莫书健(莫书健) 2007-10-25 16:43:34
cuigang的需求估计没有解决方法
陶华(陶华) 2007-10-25 16:44:57
需求评审不通过
崔刚(崔刚) 2007-10-25 16:44:40
问题本身是没有解的,但是迂回的方法有,比如IoControl或者高端中ControlModule的方法就是一种,但是不是有其它更好的办法呢?
崔刚(崔刚) 2007-10-25 16:45:34
visitor 模式也提供了一种方法,但是太麻烦,本质上和 IoControl 没什么区别。
张进(张进) 2007-10-25 16:48:45
还有一种方法,把编译器作者拉出来打,狂打,直到他搞定为止!
陈道生(陈道生) 2007-10-25 16:48:57
有点像已经知道方法名和参数,如何调用实例对应的方法,在c++中无解,在其他语言大放异彩(java,c#),都是要具有元编程能力的语言才行.
崔刚(崔刚) 2007-10-25 16:48:26
我需要一种安全方便又易于维护的办法,不要急着回答
崔刚(崔刚) 2007-10-25 16:49:23
难道我们真的离不开强制转换??
莫书健(莫书健) 2007-10-25 16:52:18
如果想调用派生类的方法,就要使用虚函数,
设计基类是要充分抽象,尽量想到种种情况
向官文(向官文) 2007-10-25 16:53:01
崔刚好像对强制转换深恶痛绝啊~
崔刚(崔刚) 2007-10-25 16:52:46
我认为依靠继承的方法不可取
莫书健(莫书健) 2007-10-25 16:53:19
实在不行,就将就强制一把,我想也可以容忍的
杨晓涛(杨晓涛) 2007-10-25 16:54:05
类本身也需要细化,寄希望一个类包含所有的接口是不现实的;
粗暴的将所有功能放在一个类中,也是不美观的,不招人喜欢的
崔刚(崔刚) 2007-10-25 16:53:45
不是我厌恶强制而是强制不安全,维护代码时可能发生不可意料的问题。
靳波(靳波) 2007-10-25 16:53:57
既然你已经有答案,不如公布出来让大家参详参详。
崔刚(崔刚) 2007-10-25 16:54:54
我没有答案,有的话就给你们上课了
莫书健(莫书健) 2007-10-25 16:56:11
这个课题就叫“崔刚猜想”吧
陶华(陶华) 2007-10-25 16:58:27
给C++标准委员会提个需求
崔刚(崔刚) 2007-10-25 16:59:27
另,dynamic_case<> 需要编译器支持 RTTI
向官文(向官文) 2007-10-25 17:04:51
C++这门静态语言解决这个执行期的动态问题恐怕无解
也许出路在第三方的接口上吧
崔刚(崔刚) 2007-10-25 17:05:55
换句话说,大家是不是认为,在目前阶段,象ControlModule和IoControl这样的接口是相对合适的喽。
崔刚(崔刚) 2007-10-25 17:13:41
2007年10月8日,汪胜平在他的工作日志里写道:
关于ControlModule型接口
这种类型的接口有以下好处:
1、降低编译依赖性。
2、如果模块A通过接口ControlModule依赖模块B,当模块B不存在时,理论上模块A仍然可以正常工作。
当然它也有一个不好的地方,那就是本来简单的调用关系,因为这个接口的引入变得复杂一些了。
但是,这两个好处需要小心使用才能保证。
1、没有函数名的依赖,但是并不代表就没有编译依赖了。比较典型的是ID的依赖。ID在模块B中被定义。在A中直接包含
B的头文件。不过这种编译依赖可以由第三方定义ID来解决。
2、如果需要传递的信息比较多,我们最容易想到的方法是定义一个结构体来传递相关信息。这个结构体如果由B来定义,
那么A又落入了在编译上依赖B的陷阱。
3、如果A在调用时没有考虑好B不存在怎样处理,那么ControlModule的第二个好处就体现不出来。
对于ControlModule型接口。我觉得要慎用,它不是万能的。某些时候,虚函数也许更方便。
=================================
这是一个典型的C方式函数。我觉得如果是通用的接口,可以用多态,如果是特殊函数,这样写纯属找茬。//崔刚 2007-10-12
=================================
ControlModule这类的接口,简单问题复杂化,难道只是为了
“降低编译依赖性”?这会不会是我们软件部附加上去的想法,
这类接口经常用于驱动接口(请参看linux\windows,
还有我们的mmos驱动模型),
我想最初这样做的理由会不会是这样:
1)为了减少接口个数。
2)统一属性设置/获取接口。
至少在驱动是这样的。
比如:
set_property1();
set_property2();
:
:
set_propertyN();
减少为一个
#define ID_PROPERTY_1 0
#define ID_PROPERTY_2 1
:
:
#define ID_PROPERTY_N n
Control( property_id, ...);
mosj
夏恒星(夏恒星) 2007-10-25 17:37:29
呵呵,原来这样的问题并不只有我遇到,俺在代码中加了注释:
//注意: 将基类指针强制转换为派生类指针并不可取
//但是此处要使用协议各层提供的非基类提供的接口
//能不能从设计上避免?
mTopProtocol = (CUartCommProtocol* )(m_pProtocolStack->GetSpecifiedLayer(UART_COMM_PROTOCOL));
崔刚(崔刚) 2007-10-25 17:44:02
这种应用更常见的地方是,把不同的派生类放到共同基类类型的指针容器里,然后把这个基类指针取出来,再干一些派生类的事情。
崔刚(崔刚) 2007-10-25 17:46:11
随便在高端里copy一段代码给大家看:
MSpinBox* pSB;
pSB = (MSpinBox*)GetWindowItem(IDC_CO_SPIN_TB_HI);
INT32 value = pSB->GetIntCur();
汪胜平(汪胜平) 2007-10-25 17:53:20
我觉得这种类型的接口比ControlModule强,
虽然这些接口会增加编译期依赖。
崔刚(崔刚) 2007-10-25 17:53:40
这就见仁见智了。
崔刚(崔刚) 2007-10-25 17:58:59
下班前,抄一段《JAVA编程思想》的话给各位共勉,包括发炎的没发炎但吃了药的。
http://vss2/sites/jhsoftware/Lists/List13/Attachments/768/ThinkJava.bmp
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
太“行而上”了!不过监护软件部真是不可小觑啊! 张进
/*******************************************/
/*
我不认为这是一个“形而上”的问题,或者一个智力游戏,它有着广泛的用途,否则不会有这么多的讨论和解决方案。昨天晚上回家查了一下,“向下转型”的确是我们要在设计中避免的,但在实际设计却经常面临,目前所有语言基本都支持到dymanicv_case<>的地步,而这却需要RTTI的支持,虽然其他高级语言比如C#,Java,Python都在语言级别提供象“反射”等方式来解决这个问题的超集,但是此种“元类”的语言特性违背了C++的精神,想必无论如何也不会出现在C++里,因为效率的牺牲和运行时存储的需求应该也不适用于目前的嵌入式设备。
反过来再想想,我们何必要求非常安全的途径解决这样的问题。C++/C本来就是一种弱类型语言,不安全的特性比比皆是,所以动态强制转换未必不是一个正途。
另外,对于汪胜平最后说出的问题,我觉得ControlModule之类的写法可以用这种强制转换的方式解决,当然孰优孰劣就留给各位去判别了。
崔刚 2007-10-26
*/