CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
【经验总结】不能实施并行处理的情况 浅谈并行编程中的任务分解模式
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C语言

damn multi-inheritance!

楼主ajoo(聪明的一猪)2002-06-02 07:01:49 在 C/C++ / C语言 提问

why?  
   
  1.   diamond   inheritance.   B:public   A;   C:public   A;   D:public   B,   C;  
  Although   C++   has   virtual   inheritance   to   solve   this.    
  But   it   is   only   making   things   much   more   complex.   Simply   a   mess.  
  He   who   can   stand   out   and   tell   how   virtual   inheritance   works,   how   the   memory   layout   would   be   after   virtual   inheritance,   please   raise   your   hand!  
   
  2.   performance   problem.  
  class   A:   public   B,   C{};  
  void   f(A*   pa){C*   pc   =   pa;}  
   
  guess   what   is   the   assembly   code   for   the   pc=pa?  
  pseudo   code:  
  is   pa   0?  
  if   yes,   move   0   to   pc;  
  if   no,   offset   pa   to   make   it   point   to   the   object   of   C.  
   
  See   what   a   simple   assignment/initialization   statement   becomes?  
  Here,   the   cost   is   much   more   than   a   simple   Mov   instruction.   the   "if"   statement   can   even   invalidate   the   instruction   prefetch!  
   
  Also,   for   virtual   functions,   the   "this"   pointer   has   to   be   moved   up   and   down   by   the   caller   and   callee.  
   
   
  3.   inheritance   itself   should   be   avoided.  
  Why?   Isn't   that   what   OO   is   for?    
  No.   Simply   put,   subtyping   (related   to   interfaces)   is   what   OO   is   really   for.    
  Subclassing   (classes),   on   the   other   hand,   should   be   used   in   a   very   disciplined   way.  
  Normally,   aggregation   should   be   used   instead.  
  To   discuss   inheritance's   cons   and   pros,   it   could   get   very   long.    
  If   who's   interested   in   that,   we   can   discuss   in   another   topic.  
   
   
   
   
  问题点数:100、回复次数:71Top

1 楼sandwish2000(紫麒麟)回复于 2002-06-02 08:49:09 得分 0

just   agree   with   you   !  
  Top

2 楼ajoo(聪明的一猪)回复于 2002-06-04 06:10:46 得分 0

anyone   else?   no   matter   you   are   against   me   or   not.   As   long   as   you   share   your   thoughts   with   us,   that's   fine.  
   
  Just   want   to   give   out   the   100   points.   Too   much   available   points!Top

3 楼huxw()回复于 2002-06-04 10:29:21 得分 0

菱形继承关系确实很糟糕,但这不是多继承的错。就好像你不能看到街上一个男人,就说他是淫棍一样;他虽然有作案工具,但是没有作案东西啊。     ;)  
       
      效率问题,我还是觉得不能避免。用了继承,就有酱紫的代价吧。  
       
      应该实现继承还是接口继承,我当然更喜欢前者。C++中这两者没有从语法上分开,失败。你说的聚合(aggregation)是不是类似mixin的概念啊?我不清楚,你解释一下先吧。   ;))  
   
  Top

4 楼kof99th(小虫)回复于 2002-06-04 10:45:56 得分 10

inside   c++   object   上说virtual   inheritance是通过一个vbtr来完成的,通过间接的寻址来完成,但是对于多重虚继承的具体实现各家编译器有各自的做法,基类对象在派生类中的位置不是在编译期决定,一切都要被推迟到执行期,所以没有效率.  
  我个人的理解,对于虚继承,编译器在背后作了太多的工作,让我们很难判断究竟发生了什么,比如通过子类取虚基类的成员时,会想到是通过指针去取的吗?还有,虚拟继承的类的构造,拷贝,赋值......,太麻烦了,如果虚基类中有成员,完全无法想象构造函数中发生了什么事,编译器居然会把你的构造函数加上额外的参数来支持它.(  
  详见<inside   the   c++   object   modal>),太恐怖了.  
  顺便想一下,如果多继承和虚拟继承一起用会怎么样?  
  所以,书中给了一个建议,不要在虚基类中声明成员变量.Top

5 楼ajoo(聪明的一猪)回复于 2002-06-04 22:24:19 得分 0

"但这不是多继承的错"  
  then   whose   fault   is   it?    
  when   you   do   multi-inheritance,   do   you   need   to   go   through   all   the   base   classes   of   the   parents   to   make   sure   they   are   not   relative?   Even   if   so,   how   do   you   prevent   the   author   of   the   base   classes   later   refactor   and   introduce   a   common   base   class?  
  In   short,   it's   not   scalable.  
   
  aggregate   is   like:  
  if   you   have   class   like  
  #define   interface   struct  
  interface   IA{public:   virtual   void   f()=0;}  
  class   A:   public   IA{...}  
  class   B:   public   A{...}  
   
  change   it   to:  
  class   B:   public   IA{IA&   a;   public:   virtual   void   f(){a.f();}}  
   
   
  Can't   agree   more   with   kof99th(小虫).   Yu   Wo   Xin   You   Qi   Qi   Yan.   :))  
  Top

6 楼Hotman_x(小人)回复于 2002-06-04 22:42:48 得分 0

C++   的优点在于:它为你提供无限可能,而到底实现哪种可能由你决定。Diamond   继承当然不可滥用,但不是不可用。标准库中的   iostream   不就用了吗?当你面对复杂的对象系统时,如果效率不是重要考虑的话,无论是组合还是接口,都不如多重继承更能准确的反映问题域的对象关系。Top

7 楼kof99th(小虫)回复于 2002-06-05 11:55:07 得分 0

呵呵,于我心有戚戚焉!  
  往深里想,就是C++的模型设计问题,他把问题留给我们,你觉得好就用,不好就不用,不就是了。任何事情都是有另外的办法可以解决的啊。Top

8 楼huxw()回复于 2002-06-05 20:18:08 得分 10

to   ajoo  
      显然是开发人员的错啊。   ;)  
   
      多继承能够更加自然的描述我们的世界,不是吗?当我们构建一个自洽的库的时候,多继承可以给库的设计带来很多便利,就如Hotman_x提到的,iostream是一个很好的例子。  
   
      aggregate确实是不错的idea,但是如果程序员一定要直接访问A的成员变量呢?我知道这不是好的做法,提出来纯粹是为了抬杠   ;)Top

9 楼huxw()回复于 2002-06-05 20:19:05 得分 0

to   kof99th  
   
        //hand    
  Top

10 楼ajoo(聪明的一猪)回复于 2002-06-05 22:47:53 得分 0

assembly   的优点在于:它为你提供无限可能,而到底实现哪种可能由你决定.  
  C   的优点在于:它为你提供无限可能,而到底实现哪种可能由你决定.  
   
  goto   的优点在于:它为你提供无限可能,而到底实现哪种可能由你决定.  
   
  "无论是组合还是接口,都不如多重继承更能准确的反映问题域的对象关系."  
  No.   multi-inheritance   normally   does   not   scale   up   well.   you   may   not   have   diamond   now,   but   later,   when   the   software   evolves,   you   may   suddenly   suffer   it.   This   is   because   subclassing   has   very   tight   coupling   between   base   class   and   sub   class.  
   
  Even   conceptually,   multi-inheritance   is   not   always   natural.   in   many   cases,   you   should   use   MI   only   for   interfaces,   not   for   implementations.  
   
  the   other   non-natural   thing   about   MI   is,   the   order   you   write   the   base   class   matters.  
  i.e.  
  class   A:   public   B,   C{}   is   different   than   class   A:public   C,   B{}  
   
   
  I   know   many   libs   use   MI.   Like   ATL.   But,   the   key   is:   you   have   to   know   how   problematic   MI   can   become   and   use   it   in   a   very   disciplined   way.   Just   as   how   people   treat   "goto".  
  Top

11 楼prototype(原型)回复于 2002-06-06 13:04:48 得分 0

>   1.   diamond   inheritance.   B:public   A;   C:public   A;   D:public   B,   C;  
  >   Although   C++   has   virtual   inheritance   to   solve   this.    
  >   But   it   is   only   making   things   much   more   complex.   Simply   a   mess.  
   
  in   what   sense,   it   is   simply   a   mess?  
   
  >   He   who   can   stand   out   and   tell   how   virtual   inheritance   works,   how   >   the   memory   layout   would   be   after   virtual   inheritance,   please  
  >   raise   your   hand!  
   
  a   common   implementation   is   using   pointers   to   the   virtual   base   in   the   non-virtual   bases.  
   
  >   Also,   for   virtual   functions,   the   "this"   pointer   has   to   be   moved  
  >   up   and   down   by   the   caller   and   callee.  
   
  this   is   a   price   you   have   to   pay   for   this   kind   of   polymorphism.   c++   compiler   implements   that   for   you.   if   you   don't   want   that,   either   don't   use   mi   (instead,   use   some   workaround   via   single   inheritance   or   whatever   methods,   but   think   about   the   price   you   have   paid   for   the   extra   code   you   did),   or   emulate   it   by   yourself.   maybe   you   can   do   it   better   for   your   special   cases,   but   it   will   be   hard   to   do   it   better   for   general   cases.  
   
  >   "但这不是多继承的错"  
  >   then   whose   fault   is   it?    
  if   you   have   to   call   it   a   fault,   imo,   it   is   the   designer's   fault,   or   if   it   is   not   the   designer's   fault,   it   the   problem's   fault.   well,   if   it   is   the   problem's   fault,   then   it   is   your   fault   to   take   the   problem,   or   it   is   your   boss'   fault   to   have   you   to   take   the   problem...   hehehe.   in   short,   mi   is   a   tech,   or   a   solution   for   some   problems,   it   is   only   bad   when   you   misuse   it.   don't   tell   me   it   is   totally   useless,   and   its   existence   is   simply   and   purely   an   evil.  
   
  >   you   have   to   know   how   problematic   MI   can   become   and   use   it   in   a    
  >   very   disciplined   way.   Just   as   how   people   treat   "goto".  
  agreed.   as   always,   it   is   dangerous   for   one   to   use   something   that   s/he   thinks   s/he   knows   it   but   actually   not.  
   
  Top

12 楼ajoo(聪明的一猪)回复于 2002-06-06 22:33:18 得分 0

prototype,   you   are   welcome   to   give   us   a   lecture   on   how   virtual   inheritance   is   implemented.  
   
  MI   is   not   totally   useless   just   as   "goto"   is   not   totally   useless.  
  But,   it   is   nearly   useless   and   almost   always   detrimental   to   the   system.   :)  
   
  Of   course,   gurus   can   use   "goto"   and   MI   smartly.   But   the   problem   is   that   there's   no   such   certificate   to   ensure   you   are   a   "guru".   While   many   people   think   they   are   "guru"   enough.  
   
  Also,   C++   is   claiming   its   performance   advantage   over   Java.   But   when   you   use   more   and   more   fancy   features   like   MI,   smart   pointer,   gc.   the   program   will   be   slower   and   slower   and   finally   similar   to   Java.  
  If   we   say   "well,   you   have   to   pay   it",   why   not   use   Java?   much   cleaner.Top

13 楼ajoo(聪明的一猪)回复于 2002-06-07 04:02:19 得分 0

prototype,   really   want   to   learn   from   you   about   the   virtual   inheritance.   I've   done   some   experiment   before   but   could   not   get   a   clear   picture   of   it.  
   
  1.  
  class   Base{  
  int   i;  
  public:  
  Base(int   a):i(a){}  
  };  
  class   Der1:   virtual   public   Base{  
  public:  
  Der1(int   a):Base(a){}  
  };  
  class   Der2:   virtual   public   Base{  
  public:  
  Der2(int   a):Base(a){}  
  };  
  class   Der3:   public   Der1,   public   Der2{  
  public:  
  Der3(int   a,   int   b):Der1(a),   Der2(b){}  
  };  
  what'll   be   the   value   of   Base::i   after  
  Der3(1,   2);?  
   
  2.  
  what   is   the   impact   of   virtual   base   class   on   virtual   functions?   what   is   the   memory   layout   of   the   following   example?  
  class   Base{  
  public:  
  virtual   void   f()=0;  
  };  
  class   Der1:   virtual   public   Base{  
  public:  
  virtual   void   f(){cout<<1<<end;}  
  };  
  class   Der2:   virtual   public   Base{  
  public:  
  virtual   void   f(){cout<<2<<end;}  
  };  
  class   Der3:   public   Der1,   public   Der2{  
  };  
   
  3.    
  what   is   the   impact   of   virtual   base   class   on   objects   without   diamond   structure?   Such   as:  
  class   Der:   virtual   public   Base{};  
  Der*   p   =   new   Der();  
   
   
  Top

14 楼ajoo(聪明的一猪)回复于 2002-06-07 04:26:37 得分 0

4.  
  also,   what   is   the   result   of:  
  Base1:   virtual   Base  
   
  Der1:   public   Base1  
  Der2:   public   Base1  
   
  Der3:   Der1,   Der2  
   
  will   Der3   have   two   copies   of   Base1   but   only   one   copy   of   Base?  
   
  5.  
  for   question   1,  
  what   is   the   memory   layout   for    
  static_cast<Base*>(static_cast<Der2*>(new   Der3()));?  
  what   is  
  static_cast<Base*>(static_cast<Der1*>(new   Der3()));?  
   
  If   they   are   the   same,   how   compiler   achieve   that?    
  Does   compiler   store   a   pointer   to   Base   in   any   object   of   Der1   and   Der2?  
  if   this   is   true.   it   seems   like   it   can   solve   the   diamond   problem   (with   performance   sacrificed).  
  But   how   can   this   work   with   the   situation   where   you   have   multple   vptr's?Top

15 楼prototype(原型)回复于 2002-06-07 04:34:16 得分 0

>   MI   is   not   totally   useless   just   as   "goto"   is   not   totally  
  >   useless.   But,   it   is   nearly   useless   and   almost   always  
  >   detrimental   to   the   system.   :)  
   
  i   am   not   sure   what   'nearly'   and   'almost'   exactly   mean   here.  
   
  >   Of   course,   gurus   can   use   "goto"   and   MI   smartly.   But   the  
  >   problem   is   that   there's   no   such   certificate   to   ensure  
  >   you   are   a   "guru".   While   many   people   think   they   are   "guru"   enough.  
   
  as   you   implied   above,   you   have   to   know   what   you   are   doing,   which  
  doesn't   mean   you   have   to   be   entitled   'guru'.   i   don't   believe  
  mi   is   in   a   position   that   no   one   can   understand   how   it   works.  
   
  >   Also,   C++   is   claiming   its   performance   advantage   over   Java.  
  >   But   when   you   use   more   and   more   fancy   features   like   MI,    
  >   smart   pointer,   gc.   the   program   will   be   slower   and   slower  
  >   and   finally   similar   to   Java.  
   
  first,   if   you   mean   using   mi,   sp,   gc   will   make   one's   c++  
  program   as   slow   as   java,   i   will   doubt   it.  
   
  >   If   we   say   "well,   you   have   to   pay   it",   why   not   use   Java?    
  >   much   cleaner.  
   
  first,   why   use   java   if   c++   is   not   worse?   and   if   you   have   to  
  emulate   mi   in   java,   how   can   you   avoid   the   overheads   for    
  general   cases?  
  Top

16 楼ajoo(聪明的一猪)回复于 2002-06-07 05:23:11 得分 0

"you   have   to   know   what   you   are   doing"  
  many   people   think   they   know   "what   they   are   doing"   when   using   "goto".   Actually   I   don't   think   "goto"   is   hard   to   understand   about   how   it   works.   :)  
   
  And,   I   believe   inheritance   can   be   even   worse   than   "goto".   With   "goto",   you   are   only   messing   around   with   your   own   function   or   module.   But   with   MI,   you   are   introducing   coupling   between   modules.   the   changes   to   the   base   class   by   other   people   can   effectively   schew   up   your   code   (this   is   called   versioning   problem).  
   
  But,   hey,   let's   stop   this   quite   phylosophical   debate.   we've   get   across   all   we   think,   right?    
  I   think   MI   is   harmful   to   software   design,   bad   performance,   complex.  
  You   think   proper   use   of   MI   is   good.   performance   tradeoff   is   worthy.  
   
  if   we   are   not   convinced   by   each   other,   don't   think   we   can   even   if   we   continue   for   ever.  
   
  Could   you   teach   me   on   the   virtual   base   class   issue?   Thanks!Top

17 楼cloudwu(云风)回复于 2002-06-07 07:17:29 得分 10

我举个例子,   要用到菱形继承的.  
  假设我想自己实现   RTTI   (仅仅是举例,有无实用价值先不考虑)  
  MFC   自己实现了   RTTI,   不过是比较丑陋的宏的形式,   如果我们完全不用  
  类似的宏,   怎么做?   我想用   template   来干,   这样就要用     MI   了.  
  我会做一个   template   RTTI<class   T,class   B>   (T   是   class,   B   是T   的基类)  
  它里面提供了   RTTI   支持的函数,   比如类似   dynamic_cast   ,   typeid,   clone   等等.  
  如果做好了,   我希望   有   A->B->C   三个类  
  我写成  
  class   A   :   virtual   RTTI<A,NUL>   {};  
  class   B   :   public   A,   virtual   RTTI<B,A>   {};  
  class   C   :   public   B,   virtual   RTTI<C,B>   {};  
  这样的形式.  
  可见,   不仅   A-B-C   是一条继承线,   RTTI<A,NUL>-RTTI<B,A>-RTTI<C-B>  
  也应该是一条继承线,   这样,   才可以回溯到基类的一些信息.  
  里面需要一些技巧,   才好实现这样一个东西  
   
  比如定义   RTTI   template   的时候,   需要类似这样的写法  
  template   <class   T,class   B>  
  class   RTTI   :   virtual   RTTI<B,B::_base>   {  
  protected:  
  typedef   B   _base;  
  };  
   
  这样才定义的出递归继承的   template  
   
  最后还需要对根上面的   template   特例化.  
   
  然后在根上面用模版方法的模式去调用虚函数,   来完成   RTTI   里面需要做的工作.    
   
  这样的实现,   是多个菱形的继承.   看起来很糟糕的说   :)  
  不过用起来比较方便,   我不知道还有没有更好的解决方法,  
  我曾经用   SI   也实现了类似的东西,   就是在   A-B-C   之间隔一个插入一个  
  RTTI   template,   但是那样局限性就大了些.  
   
  请高手指点.Top

18 楼prototype(原型)回复于 2002-06-07 07:31:08 得分 10

>   "you   have   to   know   what   you   are   doing"  
  this   is   not   restricted   to   knowing   the   syntax   and   semantics.  
  the   primary   reason   for   that   people   don't   use   'goto'  
  everywhere   is   that   they   realize   using   it   too   much   will   do  
  harm   to   the   readibility   and   maintainability.   but   if   you  
  think   a   couple   of   'goto'   will   enhance   the   r&m,   you   should  
  be   encouraged   to   do   so.   setting   a   hard   line   for   doing   this  
  while   not   doing   that   is   as   harmful   as   using   'goto'   wildly.  
  so   is   mi.   as   i   said   before,   it   is   a   tech   having   its   own  
  application   domain.   if   you   think   it   will   be   harmful   to   your  
  overall   design   and   there   is   better   solutions.   then   why  
  bother   messing   around   with   a   worse   solution?  
  whether   coupling   is   bad   or   not   is   up   to   your   design   --   what  
  you   requires   and   what   you   want   to   achieve.   single  
  inheritance   also   introduces   coupling,   people  
  can   also   screw   your   base   classes   with   si,   should   it   also   be  
  forbidden   because   of   this?  
   
  >   I   think   MI   is   harmful   to   software   design,   bad   performance,  
  >   complex.   You   think   proper   use   of   MI   is   good.   performance  
  >   tradeoff   is   worthy.  
  yeah   i   think   proper   use   of   mi   is   good.   the   claim   is   correct,  
  but   uselessly   if   'proper'   is   not   defined.   and   i   can't   define  
  it.   if   you   think   mi   should   be   forbidden,   then   please   be  
  specific   about   how   mi   can   always   be   substituted   by   a  
  better   solution   for   almost   all   design   cases.  
  as   for   performance,   i   didn't   mean   it   is   worthy.   whether    
  it   is   worthy   or   not   depends   on   a   particular   case.   what   i  
  meant   is   that   the   complexity   and   overheads   are,   as   i   see,  
  undispensible   for   a   general   mi's   implementation   no   matter  
  in   what   kind   of   languages.   it   is   surely   not   c++'s   fault.  
   
  i   have   replied   about   your   questions   about   mi   hours   ago.   but  
  when   i   came   back,   i   found   登陆信息错误!!,   even   worse   is   that  
  i   can   get   my   what   i   wrote   back.   faint.   (mozilla   is   still   a  
  sh*t.)   ok.   write   it   again   in   short:  
   
  please   don't   regard   it   as   'learning   from   me'.   i   am   not   a  
  guru,   and   i   can't   guarantee   what   i   write   will   be   all   correct  
  either,   especially   these   days   with   the   world   cup...   let's  
  learn   it   together.   please   correct   me   if   i   am   wrong,   and  
  raise   anything   you   feel   doubtful.  
   
  1.   a   compile   error.   the   virtual   base   ctor   have   to   be   invoked  
  by   the   most   derived   class.   so   either   you   do   this:  
   
  Der3(int   a,   int   b):Der1(a),   Der2(b),   base(a){}  
   
  or   define   a   default   ctor   for   'base'.  
   
  2.  
  implementation   dependent.   an   implementation   might   be   like   this:  
   
  ----------  
  ptr_2_base  
  der1  
  ----------  
  ptr_2_base  
  der2  
  ----------  
  der3  
  ----------  
  vptr   -------------------------->   vtbl:  
  base                                                           der3::f(),   delta(base)  
  ----------  
   
  3.  
  just   a   simplified   version   of   the   above   example:  
   
  -----------  
  ptr_2_base  
  der  
  -----------  
  vptr   ----------------------->   vtbl:  
  base                                                     der::f(),   delta(base)  
  -----------  
   
  4.  
  yes.  
   
  5.  
  given   the   above   examples,   you   tell   me   ba...   :-)  
   
  Top

19 楼ajoo(聪明的一猪)回复于 2002-06-07 07:32:47 得分 0

if   I   have   A:B,   C  
  you'll   need   to   write   RTTI<A,   B>,   RTTI<A,   C>,   right?  
  why   do   you   need   class   RTTI   :   virtual   RTTI<B,B::_base>     though?  
  wouldn't   this:  
  template<class   T,   class   B>  
  class   RTTI  
  {  
  typedef   B   Base;  
  typedef   RTTI<B,   B::Base>   MetaBase;  
  };  
  work?  
  Top

20 楼ajoo(聪明的一猪)回复于 2002-06-07 07:38:36 得分 0

class   Base{  
  public:  
  virtual   void   f()=0;  
  void   g(){f();}  
  };  
  class   Der1:   virtual   public   Base{  
  public:  
  virtual   void   f(){cout<<1<<end;}  
  };  
  class   Der2:   virtual   public   Base{  
  public:  
  virtual   void   f(){cout<<2<<end;}  
  };  
  class   Der3:   public   Der1,   public   Der2{  
  };  
  what   is   the   result   of   new   Der3()->g();?Top

21 楼ajoo(聪明的一猪)回复于 2002-06-07 07:40:38 得分 0

"1.   a   compile   error.   the   virtual   base   ctor   have   to   be   invoked  
  by   the   most   derived   class.   so   either   you   do   this:  
   
  Der3(int   a,   int   b):Der1(a),   Der2(b),   base(a){}  
   
  or   define   a   default   ctor   for   'base'.  
  "  
  don't   quite   understand.   how   can   a   default   ctor   work?   what's   the   semantics?Top

22 楼cloudwu(云风)回复于 2002-06-07 08:11:27 得分 10

我的例子可能举的不好,   或者说的不清楚  
  RTTI   template   里面还需要有虚函数,   来完成一些扩展的功能.  
  比如模拟一个   dynamic_cast   来检查  
  downcast   是否有效.   比如检查一个   A*   是不是指向的一个   B   对象  
   
  独立的为A,B,C   每个   类给一个   RTTI   template   是不够的.  
   
  我实际中类似问题并不是用来解决   RTTI   的,   比   RTTI   复杂的多.  
  这里用   RTTI   举例,   也没有仔细考虑是否恰当.Top

23 楼prototype(原型)回复于 2002-06-07 09:27:57 得分 10

what   is   the   result   of   new   Der3()->g();?  
   
  compile   time   error   due   to   ambiguity.  
   
  how   can   a   default   ctor   work?   what's   the   semantics?  
   
  make   a   correction:  
   
  Der3(int   a,   int   b):Der1(a),   Der2(b),   base(a){}  
   
  should   be  
   
  Der3(int   a,   int   b):base(a),   Der1(a),   Der2(b)   {}  
  .  
   
  ------------------------  
  Der3(int   a,   int   b):base(),   Der1(a),   Der2(b)   {}    
                                        ^^^^^^  
  automatically   added   by   the   compiler   if   you   have   such   a   ctor   for   'base'.  
   
  Top

24 楼ajoo(聪明的一猪)回复于 2002-06-07 22:13:17 得分 0

new   Der3()->g();   compile   error?  
  g()   is   not   virtual   ah.  
  what   if   I   say  
  Base*   pb   =   new   Der3();  
  will   that   work?  
  then   pb->g();   what   is   the   result?  
   
  if   you   say    
  Der3(int   a,   int   b):base(),   Der1(a),   Der2(b)   {}    
  and   the   ctor   of   Der1   and   Der2   will   also   call   base(a)   and   base(b),   what   is   the   semantics?  
  Top

25 楼ajoo(聪明的一猪)回复于 2002-06-07 22:14:23 得分 0

cloudwu(云风):  
  but   without   a   macro   or   language   support,   how   can   you   generate   typeid?   with   macro,   you   can   say   #type   to   get   a   string.Top

26 楼ajoo(聪明的一猪)回复于 2002-06-07 22:24:40 得分 0

prototype:    
  I   don't   think   the   ban   of   "goto"   has   been   harmful   so   far.   don't   think   the   ban   of   "pointer"   in   Java   has   been   harmful.   if   a   thing   is   90%   bad,   and   100%   replacable,   don't   see   why   banning   it   would   be   harmful.  
   
  Let   me   reorganize   my   points:  
  1.   inheritance   itself   has   to   be   used   with   caution   because   of   coupling   problem.  
  2.   MI,   besides   the   problem   of   inheritance,   also   has   performance   problem   and   diamond   inheritance   problem.   Although   C++   gives   a   virtual   base   class,   the   solution   is   not   elegant   and   complex.  
   
  But,   you   raised   a   good   point:   how   do   we   replace   mi   or   inheritance   in   general   situation?  
   
  Well,   I   believe   aggregation   is   good   enough   here,   as   many   people   already   said.  
  Do   you   like   to   discuss   the   cons   and   pros   of   aggregation?   I   think   that's   more   detailed   and   easier   to   draw   a   conclusion   than   simply   arguing   "ban   MI   vs.   proper   use   of   MI   is   necessary".Top

27 楼cloudwu(云风)回复于 2002-06-07 23:10:28 得分 0

ajoo(jet   pig)    
  你完全可以在   template   里声明一个   static   变量.  
  每个类的   cpp   里都为这个变量初始化一个静态值,(dword   或   string)  
  如果你不给出,   编译器是不会让你过的,   所以是强制叫你写了   :)  
  这样比用MFC里类似的宏好的多.  
  ps.   我只是用   RTTI   举例.Top

28 楼ajoo(聪明的一猪)回复于 2002-06-08 01:46:35 得分 0

but   then   programmer   is   responsible   for   the   uniqueness   of   the   id?  
  too   hard   bah?Top

29 楼prototype(原型)回复于 2002-06-08 05:14:27 得分 10

>   new   Der3()->g();   compile   error?  
  sorry,   i   didn't   make   it   clear.   class   'der3'   has   ambiguity.  
  in   the   case   like   your   example,   der3   must   have   a   'f()'   to  
  override   the   'f()'s   from   the   bases,   since   at   the  
  time   of   figuring   out   the   vtbl   for   der3,   the   two   'f()'s   from  
  'der1'   and   'der2'   have   the   same   priority   to   be   chosen.   so  
  the   compiler   won't   let   you   have   such   a   definition   for   'der3'.  
   
  >   if   you   say    
  >   Der3(int   a,   int   b):base(),   Der1(a),   Der2(b)   {}    
  >   and   the   ctor   of   Der1   and   Der2   will   also   call   base(a)   and   base(b),  
  >   what   is   the   semantics?  
  oh.   the   base's   ctor   will   be   invoked   by   the   most   derived   class.  
  the   calls   to   the   base's   ctor   in   'der1'   and   'der2'   will   be   ignored.  
  that   is   why   (at   least   in   part)   the   'base'   is   called   'virtual'?  
   
  my   view   of   banning   "goto"   is   actually   banning   bad   coding  
  styles   that   makes   r&m   very   difficult,   not   simply   "goto"  
  itself.   fortunately,   almost   every   suggestion   against   goto  
  has   this   reason   stated   (if   not,   the   suggestion   would   be  
  meaningless).   to   me,   the   ban   of   'goto'   is   still   not   rigorous,   but  
  flexible   due   to   that.   in   reality,   i   do   have   a   few   functions  
  that   use   'goto',   making   my   code   short   and   clean.  
  another   "bad"   thing   in   c++   is   'macro',   which   is   also   labeled  
  'evil'   due   to   various   reasons.   but   it   is   still   used   widely  
  and   smartly   everyday.   why?  
  i   am   not   saying   we   should   stick   to   what   we   have   now,   never  
  evolve   our   methods   and   tools.   i   am   trying   to   say:   we   must  
  understand   the   true   motivation   for   a   piece   of   suggestion,  
  instead   of   saying   or   accepting   it   as   "this   is   a   law,   violation  
  will   be   punished",   since   this   is   harmful,   no   doubt   about   it.    
   
  >   Let   me   reorganize   my   points:  
  >   1.   inheritance   itself   has   to   be   used   with   caution   because  
  >   of   coupling   problem.  
  sure.  
   
  >   2.   MI,   besides   the   problem   of   inheritance,   also   has   performance  
  >   problem   and   diamond   inheritance   problem.   Although   C++   gives   a  
  >   virtual   base   class,   the   solution   is   not   elegant   and   complex.  
  imho,   the   performance   problem   of   mi   is   not   that   much.   for  
  the   scheme   presented   above,   compared   to   si,   the   extra   overheads   are:  
  1.   One   addition   operation   for   each   use   of   a   member   in   the  
  2nd   or   subsequant   base(s).  
  2.   One   memory   reference   and   one   addition   operation   for  
  accessing   the   members   in   the   virtual   base.    
  3.   One   test   and   one   increment   for   assignments   between   pointers   to  
  bases   and   those   to   the   derived.  
  (anything   else   i   missed   here?)  
  is   that   too   much?   i   would   say   that   is   not   very   much   (at   leas)  
  for   most   cases.  
   
  >   Well,   I   believe   aggregation   is   good   enough   here,   as   many  
  >   people   already   said.  
  one   problem   about   aggregation   is   that   it   is   harder   to   understand.  
  for   many   cases,   inheritance   sounds   more   like   a   natural   choice.    
  but   no   doubt,   aggregation   is   more   flexible.   do   you   think  
  aggregation   can   replace   mi   for   all   general   cases?   i   am   not   sure.  
   
  Top

30 楼ajoo(聪明的一猪)回复于 2002-06-08 06:11:14 得分 0

"do   you   think   aggregation   can   replace   mi   for   all   general   cases?"  
  Yes,   I   do.  
  Actually   Java's   success   already   proves   that.  
   
  1.   One   addition   operation   for   each   use   of   a   member   in   the  
  2nd   or   subsequant   base(s).  
  yes.    
  2.   One   memory   reference   and   one   addition   operation   for  
  accessing   the   members   in   the   virtual   base.    
  yes.  
  3.   One   test   and   one   increment   for   assignments   between   pointers   to  
  bases   and   those   to   the   derived.  
  yes.   this   is   for   any   MI,   not   just   diamond.  
   
  But   this   is   quite   something.  
  it   happenes   not   only   on   explicit   assignment.  
  All   subsumption   will   suffer   this.   and   subsumption   is   the   way   you   do   OO   programming.   you   do   it   everwhere.  
  a   simple   assignment   becomes   such   a   big   thing   is   both   inefficient   and   non-elegant.   (changing   the   pointer's   actual   value   on   upcasting   is   a   bad   hack!   Confusing   and   dangerous.   it   also   makes   gc   algorithms   harder   from   my   understanding   of   gc.)    
   
  by   the   way,   macro   is   still   useful   because   sometimes   we   really   don't   have   any   good   way   around.   While   if   we   can   write   it   in   function,   it   should   be   forbidden   to   write   macro   though.  
  for   "goto",   although   people   always   claim   that   they   need   "goto",   I   never   see   one   case   where   you   don't   have   good   way   around.   Just   as   that   discussion   about   exiting   for-loop,   a   function   can   simply   solve   it.  
   
  for   MI,   since   aggregation   is   a   good   way   around,   can't   see   any   reason   for   using   MI.  
   
  Top

31 楼cloudwu(云风)回复于 2002-06-08 12:38:16 得分 0

ajoo(jet   pig):  
  程序员是有责任提供唯一的   id,   这并不是难事.  
  可以用和类名相同的   string.   不要告诉我你会敲错string?  
  用宏来实现也是一样,   除非是机器生成的代码.    
  只要不用编译器提供的   RTTI,   自己来做,   最后都有提供  
  如何提供唯一   id   的问题.  
   
  甚至可以用一个特殊的集合来管理   id,   运行时,   在静态初始化的时候  
  可以检查全局的   id   是否有重复,   避免这个问题手段多着呢.  
   
  ps.这已经偏离了是否用   MI   的问题,   我认为C++标准委员会把  
  MI   的支持纳入标准有它存在的必要.   比如我举的这个例子,  
  个人并不认为别的方法解决会更漂亮一些.   如果你只认识到   MI  
  对你的程序会带来复杂度和不可控因素,   而引申到    
  "can't   see   any   reason   for   using   MI"   那就有必要从新虚心当好  
  学生,   好好再学习一下   C++   了.    
  建议重读一遍   D&E,   MI   存在的理由很充分.Top

32 楼huxw()回复于 2002-06-08 20:52:06 得分 10

虚拟多继承结构并非这样难以理解。  
   
  当class   A   :   public   virtual   Base{};时,为了表示A虚拟继承了Base,A中存在一个指针,(间接)指向Base这个子类型的位置。同理假设class   B   :   public   virtual   Base{};,然后class   C   :   public   A,   public   B{};这个时候,A中间接指向Base的指针和B中简介指向Base的指针最终指向的目的是相同的,同一个Base。至于如何间接指向,和编译器相关,除非要二进制重用你的代码,否则应该没有需求知道。   ;)  
   
  虚拟继承带来的空间代价,每一个虚基类需要一个指针,为了实现虚拟继承可能需要一个额外的指针(也可能不需要)。时间代价,类型转化的时候需要一次多余的指针访问动作。  
   
  我不认为mi是那么糟糕的东西,具体的内容,d&e里面说得很多了,你不希望我抄书吧。java也无非是一种妥协,没必要看得起这种妥协,看不起另一种妥协吧。Top

33 楼ajoo(聪明的一猪)回复于 2002-06-10 23:15:10 得分 0

cloudwu(云风)   :  
  show   how   do   you   use   MI   to   implement   your   requirement.  
  It's   good   to   have   something   we   can   discuss   "whether   we   have   to   use   MI".  
   
  Although   I   don't   like   your   way   of   giving   type   id.   you   rely   on   the   dynamic   check   instead   of   the   type   system.   programmers   are   human   being.   what's   wrong   if   I   typo   the   class   name?   "Useramne"   for   "Username"?   the   design   is   so   delicate   bah?  
   
   
  Top

34 楼ajoo(聪明的一猪)回复于 2002-06-10 23:23:55 得分 0

by   the   way,   if   we   allow   programmers   to   input   the   type   name   individually,   why   don't   we   just   let   them   implement   the   few   rtti   functions?  
  class   A{  
  class   TypeInfoImpl:   public   TypeInfo  
  {  
  public:  
      const   char*   getTypeName()const{return   "A";}  
      const   TypeInfo&   getSuperType()const  
      {return   B::getTypeInfo();}  
  };  
  static   TypeInfoImpl   info;  
  public:  
  static   const   TypeInfo&   getTypeInfo()const{return   info;}  
  };  
   
  Overall,   the   MI   only   saves   you   a   few   lines   of   keystrokes.Top

35 楼ajoo(聪明的一猪)回复于 2002-06-11 01:16:57 得分 0

macro   cannot   solve   the   name   problem?  
  Well,   it   cannot   work   for   "namespace".   But,   don't   insult   my   dear   macro.   :)  
  #name   can   easily   give   the   right   name,   no   worry   about   typo.  
   
  "如果你只认识到   MI  
  对你的程序会带来复杂度和不可控因素,   而引申到    
  "can't   see   any   reason   for   using   MI"   那就有必要从新虚心当好  
  学生,   好好再学习一下   C++   了.    
  建议重读一遍   D&E,   MI   存在的理由很充分."  
  Pa   pa   ya!   Thank   you   you   did   not   say   "then   you   are   the   enemy   of   the   people"   :)  
   
  what's   "D&E"?   a   book?  
  why   do   you   guys   always   give   either   some   famous   name   or   famous   book   in   discussion?   Don't   you   think   by   yourself?  
  Top

36 楼huxw()回复于 2002-06-11 09:44:01 得分 0

D&E是一本书,C++的发展与演化(The   Design   and   Evolution   of   C++)。是C++的发明者Bjarne   Struostrup自己写的一本书。其中专门有一章谈论mi为什么被引入C++。  
   
  确实mi问题让B.S.自己说明比我们要有力的多   ;)  
   
  Top

37 楼anrxhzh(百宝箱)回复于 2002-06-11 10:39:22 得分 10

毫无疑问,MI应该谨慎使用。不过我对另一个问题更感兴趣,MI能够被aggregation完全地替代吗?这里“替代”一词的含义是:  
  1.aggregation能够实现MI的功能。  
  2.这种实现相对于MI更容易理解,更简洁。  
  聪明人会意识到,这是一个容易证伪不易证实的命题。因为即使发现在1000种情况下都满足条件,只要发现一种不满足条件的特例,就可以推翻这个命题。既然ajoo(jet   pig)判了MI的死刑,我们的审判当然要实行无罪原则。下面我举一个特例,请陪审团注意:  
  设计一个射击游戏,背景是西部牛仔的决斗。我们有一个类型Comboy,表示牛仔的各种拔枪动作,另一个类型Window,表示游戏中的移动的窗口,可以用MI来定义一个类型ComboyWindow,表示运动中的牛仔,这个类型可以用在所有要求Comboy或者Window的场合中。Top

38 楼cloudwu(云风)回复于 2002-06-11 13:20:38 得分 0

"the   MI   only   saves   you   a   few   lines   of   keystrokes"  
  我并不认为如此,   继续   RTTI   的例子,   不错,   你可以在每个类里多加几个小函数  
  来实现,   但是日后,   对   RTTI   的要求提高的怎么办?  
  比如你想让每个类都支持从一个   string   new   出它自己.    
  你是选择再给每个类增加一个小函数,   还是开始就用   MI   来设计,  
  而只在   RTTI   的   template   里加一个函数?  
   
  anrxhzh(百宝箱)   :  
  我对游戏的例子很有兴趣;)   (因为我是专职做这方面的   R&D   的)  
  对于   牛仔   和   移动   的问题,   在我现在的     engine   里倒不是用   MI   解决的.  
  我把   牛仔   和   移动   分别做成了两个类.   (分别继承于   atom   和   control)  
  而   atom   和     control   也有共同的基类   object  
  而在这个   engine   里   control   可以   bind   到   atom   上,   一个   object  
  可以   bind   上很多   control.   这样在运行时的绑定,   要比用   MI   设计一个新类  
  更实用一些.  
   
   
  Top

39 楼anrxhzh(百宝箱)回复于 2002-06-11 14:24:20 得分 0

久仰云风工作室的大名:-)  
   
  多继承在C++中有两种用途:  
  1.通过粘合两个逻辑上无关的类型来构造一个新的类型。  
  2.连接类型的接口和实现。  
  大部分对MI的争议都是针对1.的,牛仔的例子属于2.,拔枪属于牛仔的接口,控制引擎属于实现细节。我认为对于2.还是使用委托更加灵活一些。一般来讲,2.在对设计有完全的控制权时使用,1.在粘合两个来自第三方的类层次时使用。Top

40 楼anrxhzh(百宝箱)回复于 2002-06-11 14:33:46 得分 0

补充一句,千夫所指的钻石问题在2.的情况下才可能出现。我觉得对MI更加严峻的质疑应该来自1.Top

41 楼anrxhzh(百宝箱)回复于 2002-06-11 14:50:37 得分 0

更加严峻的质疑:水陆坦克是否应该从战船和战车中多重继承?Top

42 楼cloudwu(云风)回复于 2002-06-11 14:53:32 得分 0

赞同:)   我仔细用   MI   时间不长,   主要也是用作"连接类型的接口和实现"  
  用委托的话,   总觉得不如   MI   叙事自然   :(  
   
  我现在做的   engine,   也尽量想把对象和显示分开.   就是把自绘部分从对象  
  中隔离开.   感觉使用   MI   会自然一些.Top

43 楼cloudwu(云风)回复于 2002-06-11 14:56:49 得分 0

其实还有个过度设计的问题,   比如通常我们设计的每个对象都有一个   clone()  
  函数,   返回自己的指针.   这个函数是否应该用   template   +   MI   来实现.  
  算不算过度设计?Top

44 楼anrxhzh(百宝箱)回复于 2002-06-11 16:36:57 得分 0

>>千夫所指的钻石问题在2.的情况下才可能出现。  
   
  这句话考虑得不太周详。假如水陆坦克从战船和战车继承,战船从战和船继承,战车从战和车继承的话,迷人的钻石就又出现了。  
   
                                                车             战             船  
                                                    \         /     \         /  
                                                      战车         战船  
                                                              \     /  
                                                            水陆坦克  
                                                               
                                                           
  Top

45 楼liu_feng_fly(笑看风云 搏击苍穹 衔日月)回复于 2002-06-11 16:55:48 得分 0

呵呵,有得必有失,就好象java,为了跨平台,弄了一个虚拟机出来,结果跨平台有了,效率却没有了。多重继承也是这样,当你考虑使用他的时候,首先要考虑的就是你是否能负担使用他给你带来的那些正面的和反面的效果。  
  c++从来都不强制你使用什么,当然,他也没有理由强制人们不使用多重继承,如果不喜欢,不用就是了,呵呵。Top

46 楼huxw()回复于 2002-06-11 18:46:46 得分 0

to   anrxhzh(百宝箱)  
      如你自己说的。mi当归并两个对等,逻辑无关的类型的时候,是很直白的。但是你给的水路坦克的例子,显然战车和战船不满足这样的要求。  
   
      对于这样一个问题,我觉得只有单根继承一种方法。不是单根的系统,就没法淘汰mi。你觉得呢?Top

47 楼ajoo(聪明的一猪)回复于 2002-06-11 23:15:37 得分 0

cloudwu(云风):  
  actually,   don't   you   see   that   my   TypeInfo   class   can   be   templatized   so   that   all   you   need   to   do   is   to   say:  
  static   TypeInfoImpl<B,   A>   info;  
   
  then   all   the   changes   can   be   made   to   the   TypeInfoImpl   without   the   user   changing   anything?  
  Also,   macro   can   be   used   so   that   all   you   have   to   write   in   your   class   is:  
  DECLARE_RTTI(B,   A)  
   
  but,   that's   quite   like   MS's   solution.  
   
   
  anrxhzh(百宝箱)   :  
  车             战             船     战车         战船   水陆坦克  
  simplistically,   they   can   be   declared   as   independent   interfaces.  
  Well,   you   may   say   that   战车   is   a   车   and   战.  
  OK.   two   approaches,    
  1.   CombatVehicle   is   subtype   of   Vehicle   and   Combatable.   (note,   here,   MI   is   only   used   for   interface,   not   implementation)  
  2.   use   adapters.   CombatVehicle2Vehicle,   CombatVehicle2Combatable.  
  only   a   few   more   lines   of   keystrokes.  
   
  as   for   the   implementation   classes,   use   aggregation   too.  
   
  What's   impossible   here?  
   
  although   you   need   more   keystrokes,   but   it   does   reduce   the   system   coupling.Top

48 楼ajoo(聪明的一猪)回复于 2002-06-11 23:21:14 得分 0

"比如通常我们设计的每个对象都有一个   clone()  
  函数,   返回自己的指针.   这个函数是否应该用   template   +   MI   来实现.  
  算不算过度设计?"  
  hey,   how   do   you   write   a   general   clone?   use   copy-con?   but   that's   not   general   enough.Top

49 楼cloudwu(云风)回复于 2002-06-12 00:03:37 得分 0

我曾经是疯狂的宏爱好者,    
  但是,   template   的出现可以替代   99.9%   的宏运用  
  MFC   的   RTTI   宏其实是很拙劣的实现方法,   其背景是早期的   VC   对  
  template   支持的很差.  
   
  宏并不利于代码的阅读的调试,   甚至没有代码自动生成器的时候.  
  DECLARE_RTTI(B,   A)   这种写法,   不小心把基类名写错是很危险的.  
  ps.   切身体会,   我早几年的一个东西里的设计就是用这样的宏实现的,  
  copy-paste   代码的时候,   曾经写错过.  
   
  但是合理的组织   template   可以在编译时报出这种错误,  
  毕竟   宏在编译时做的事情比   template   少的多.  
   
  同样宏没有特例化的机制,   所以你在维护   RTTI   的代码时需要对基类  
  单独维护一个版本.   而用   template   ,   可以把单独的几个不同的地方  
  特例化出来.  
   
  而且宏的修改和调试都是相当麻烦的.   在你为你的   RTTI   宏增加一个功能  
  插入一个函数的时候,   会异常痛苦.   (需要宏展开,   然后调试)  
   
  虽然   MFC   很伟大,   不要被   MS   的东西束缚住思想了.    
   
  关于   clone()   函数,   一个简单的版本如下:  
   
  template<class   T>  
  class   implement_clone  
  {  
        public:  
        T*   clone()   const   {   return   new   T(*this);   }      
  };  
   
  class   A   :   public   implement_clone<A>  
  {  
  ....  
  };  
   
  复杂的,   可能还要用   traits   技术.  
   
  只是举个例子而已.   不必拘泥于例子本身.  
   
   
   
   
  Top

50 楼ajoo(聪明的一猪)回复于 2002-06-12 00:29:56 得分 0

either:  
  #define   DECLARE(B,   A)   private:   static   TypeInfoImpl<B,   A>   info;   pubic:   static   TypeInfo&   getTypeInfo(){return   info;}  
   
  or   write   it   explicitly.  
   
  you   choose   by   yourself.  
  But   don't   think   here   you   can   use   any   template   to   replace   macro.    
   
  But   anyway,   this   can   replace   your   favorite   MI.  
   
  Also,   if   you   just   use   copy-ctor   to   implement   a   common   clone,   it's   typical   over   design.   Don't   do   that.   useless   at   all.  
   
  even   if   you   insist   on   that,   don't   use   MI.   use   aggregation   instead.Top

51 楼ajoo(聪明的一猪)回复于 2002-06-12 01:22:24 得分 0

here   the   entire   solution:   (not   compiled   yet,   but   guess   you   should   know   what   it's   doing)  
   
  //typeinfo   interface  
  struct   TypeInfo{  
  virtual   const   char*   getTypeName()   const=0;  
  virtual   const   TypeInfo&   getSuperType()const=0;  
  };  
  //rtti   interface   which   all   rtti   class   should   implement.  
  struct   Rtti{  
  virtual   const   TypeInfo&   getTypeInfo()const=0;  
  };  
  //the   template   class   that   has   most   of   our   logic   in.  
  template<class   D,   class   B>  
  class   TypeInfoImpl:   public   TypeInfo  
  {  
  public:  
  virtual   const   char*   getTypeName()   const{return   name;}  
  virtual   const   TypeInfo&   getSuperType()const{return   B::getTypeInfo();}  
  TypeInfoImpl(const   char*   n):name(n);  
  private:  
  const   char*   const   name;  
  };  
  //the   top   rtti   class  
  struct   Top   :   public   Rtti{  
  virtual   const   TypeInfo&   getTypeInfo()const   {return   _type;}  
  private:  
  class   Type:   public   TypeInfo{  
  virtual   const   char*   getTypeName(){return   "Top";}  
  virtual   const   TypeINfo&   getSuperType()const{throw   "Top   type   does   not   have   super   type";}  
  }  
  static   Type   _type;  
  };  
  //hate   macro?   type   it   explicitly   then.   not   a   big   deal.  
  #define   DECLARE_RTTI(D,   B)   private:   static   TypeInfoImpl<D,   B>   _typeinfo;   \  
  public:   virtual   const   TypeInfo&   getTypeInfo()const{return   _typeinfo;}  
   
  //c++   does   not   allow   inline   initlization   of   static   variable.   Damn!  
  #define   INIT_RTTI(D,   B)   TypeInfoImpl<D.   B>   D::_typeinfo(#D)  
   
  //here   go   the   demos  
  class   Base{  
  DECLARE_RTTI(Base,   Top)  
  };  
   
  class   Der:   public   Base{  
  DECLARE_RTTI(Der,   Base)  
  ......  
  };  
   
  INIT_RTTI(Base,   Top);  
  INIT_RTTI(Der,   Base);  
   
   
  Top

52 楼cloudwu(云风)回复于 2002-06-12 01:57:30 得分 0

你仅仅提供了   getTypeInfo,    
  如果我要求把  
  getTypeName()  
  getSuperType()  
  都可以直接调用,   而不用令人困扰的  
  getTypeInfo().getTypeName()   这样的方式调用呢?  
   
  那是不是要在宏里再加上一些   inline   函数  
  如果有更多的要求,   是不是要再加呢,   最后你的宏会越来越庞大.  
   
  那仅仅只是把本该写到另一个类,   然后继承下来的东西.  
  用宏的方式放到单一的类去了.用手工的方法,   把两个类写成了一个.  
   
  不错,   是可以用   aggregation   代替   MI  
  那么用   C   也可以自己做   vtable,   我可以用  
  object->vtbl->func()   在   C   里通过   struct   来模拟   C++   的多态,  
  为什么还要用   C++   呢?  
   
  Top

53 楼ajoo(聪明的一猪)回复于 2002-06-12 01:59:55 得分 0

Oops,   TypeInfoImpl   should   be  
  template<class   D,   class   B>  
  class   TypeInfoImpl:   public   TypeInfo  
  {  
  public:  
  virtual   const   char*   getTypeName()   const{return   name;}  
  virtual   const   TypeInfo&   getSuperType()const{return   B::_getTypeInfo();}  
  TypeInfoImpl(const   char*   n):name(n);  
  private:  
  const   char*   const   name;  
  };  
   
  _getTypeInfo   should   be   a   static   method   defined   in   each   rtti   class.  
  Top

54 楼ajoo(聪明的一猪)回复于 2002-06-12 02:02:54 得分 0

令人困扰的?   why?   because   of   several   more   keystrokes?  
  Do   you   mean   you   want   to   put   all   functionalities   into   one   class?  
   
  用   C   也可以自己做   vtable  
  Tai   Gong   le   bah?  
   
  as   i   said,   MI   and   even   inheritance   has   coupling   problem,   so   we   avoid   using   them.   Can   you   give   your   reason   why   you   have   to   have   MI?   what's   the   benefit   of   Mi   over   aggregation?   except   less   keystrokes?Top

55 楼cloudwu(云风)回复于 2002-06-12 02:07:36 得分 0

都说了clone只是举例了.   在真实应用中,   可能还要干很多事情.  
  也可能是通过   Factory   来构造,    
  还可能需要改变引用计数,   向容器注册,   等等.  
   
  这些都是可变的因素,   如果日后想修改怎么办?   还用宏吗?  
   
  Top

56 楼ajoo(聪明的一猪)回复于 2002-06-12 02:10:34 得分 0

we   have   to   discuss   specific   requirements   to   do   design.   Without   the   requirement,   it's   of   no   use   to   talk   about   design.    
  so   rather   keep   it   simple.Top

57 楼cloudwu(云风)回复于 2002-06-12 02:11:11 得分 0

我使用   MI   并不多,   所以研究也不深刻,   但是在很多情况下,  
  我都是希望能够用更直观的方式来表述问题.  
  就好象刚才   c->ctbl->func()   的极端例子,   难道我们用   C++  
  仅仅只是想少敲点键盘?Top

58 楼ajoo(聪明的一猪)回复于 2002-06-12 02:13:14 得分 0

as   I   said,   you   are   Tai   Gang.   :)  
  c++   has   benefits.   you   don't   want   me   to   list   them,   right?  
   
  While,   MI   has   problems,   and   is   replacable.    
  my   point   is:  
  1.   MI/inheritance   has   problem  
  2.   aggregation   can   replace   them   and   is   more   flexible.Top

59 楼cloudwu(云风)回复于 2002-06-12 02:14:50 得分 0

如果有那个需求了,   关于那个会复杂一些的   clone   的实现,  
  用一下   MI,   真的很糟糕吗?  
   
  如果两类对象的   clone   实现方法不同.  
   
  我们又需要统一管理,   增加一个虚基类,  
  导致的“菱形"   真的很可怕吗?  
  Top

60 楼ajoo(聪明的一猪)回复于 2002-06-12 02:18:03 得分 0

直观?   what   is   直观?  
  obj.getTypeInfo().getTypeName()   is   not   as   直观   as  
  obj.getTypeName()?  
  it's   hard   to   say   which   one   is   better.  
  From   my   point   of   view,   certainly   the   first   one!  
   
  Let's   use   your   assumption,   later,   we   may   want   to   add   a   function   called   "getSiblings()";  
  Well,   if   you   do   inheritance,   what   if   somebody   already   defined   this   function   in   their   class?   Remember,   you   are   not   the   only   one   using   this   Rtti   lib.   don't   want   them   to   yell   at   you   "what   the   heck   are   you   doing?!"?  
  Do   you   know   of   "versioning   problem"?  
  Top

61 楼ajoo(聪明的一猪)回复于 2002-06-12 02:19:13 得分 0

用一下   MI,   真的很糟糕吗?  
  yes.   normally   you   system   cannot   scale   well   with   MI   in   existence.  
   
  also,   use   aggregation   to   replace   MI   真的很糟糕吗?  
  Top

62 楼cloudwu(云风)回复于 2002-06-12 02:28:48 得分 0

或许   clone()   的里子还不够好,  
  换个例子了.  
   
  如果我有很多类的对象,   它们可以用各种方式运动.  
  其中我完成了一种曲线运动,    
  我希望有一个曲线运动的对象.  
  直观的方法是从这个对象和曲线运动两者继承下来.  
  我也可以把曲线运动换成直线运动.  
   
  当我的容器驱动所有的对象开始运动的时候,   只需要对这些东西  
  发一个   move   的指令.  
  如果用聚合,   你需要给对象增加一个函数,   转发   move  
  当我再增加一个别的行动方式,   比如旋转时,   采用聚合  
  就需要再增加一个转发旋转的函数.  
   
  但是直接用   MI   组合在一起的话,   就可以不需要这个工作,  
  而且表述问题更清楚.  
  Top

63 楼ajoo(聪明的一猪)回复于 2002-06-12 02:34:24 得分 0

in   your   case,  
  an   interface   like  
  struct   Moveable{  
        virtual   void   move()=0;  
  }  
  should   be   used.  
   
  then   for   the   impl,   as   I'm   not   sure   about   your   requirement,   can't   give   detailee   solution,   but,   I   can   tell   you,   you   won't   have   to   use   MI.  
  Top

64 楼cloudwu(云风)回复于 2002-06-12 02:34:34 得分 0

当名字混淆的时候,   可以用  
  obj.rtti::getTypeName()  
  在   RTTI   template   里   typedef   一个   rtti   就   ok   了.  
  不发生混淆的时候,   直接用既可.  
  你这样通过   obj.getTypeInfo().getTypeName()   的方式调用函数  
  其实比编译器优化过的   MI   的实现效率上更低.  
   
  而另一个   clone   的问题呢?  
  你希望   obj.getCloneInterface().clone()   这样去调用吗?Top

65 楼cloudwu(云风)回复于 2002-06-12 02:36:51 得分 0

的确,   我会有一个  
  struct   Moveable{  
        virtual   void   move()=0;  
  }  
   
  的界面.   如果需要的时候,   我会向这个界面添加新的东西.  
  虽然整个工程可能要重新编译,   但是代码的改动是很小的.  
   
  当然也可以留下接口,   用   visitor   模式来扩展.  
   
   
  Top

66 楼ajoo(聪明的一猪)回复于 2002-06-12 02:41:18 得分 0

that   guy   yelled   at   you   used   getSibling()   for   a   month.   It   always   works.   Now,   with   your   new   version,   he   has   to   change   his   code   to   say    
  MyClass::getSibling()?  
   
  also,   a   typical   versioning   problem   is   like:  
  struct   A{  
  virtual   void   f()=0;  
  };  
   
  class   B:public   A{  
  public:  
  virtual   void   f(){......}  
  void   getSiblings(){......}  
  };  
   
  And   all   the   sudden,   you   add   a   virtual   function   getSibling()   to   A,   the   compiler   won't   complain,   and   you've   silently   ruined   class   B!!!Top

67 楼ajoo(聪明的一猪)回复于 2002-06-12 02:43:27 得分 0

Again,   as   long   as   you   implement   the   functionality.   It's   of   no   point   to   require   that   the   function   has   to   be   within   a   given   class.  
   
  the   user   should   not   care   whether   call   obj.getTypeName()   or   obj.getTypeInfo().getTypeName()   at   all!  
   
  as   for   your   clone,   it's   over   design,   without   specific   requirement,   I   would   not   implement   it   at   all.  
  Top

68 楼cloudwu(云风)回复于 2002-06-12 02:50:50 得分 0

"that   guy   yelled   at   you   used   getSibling()   for   a   month.   It   always   works.   Now,   with   your   new   version,   he   has   to   change   his   code   to   say    
  MyClass::getSibling()?"    
  如果它需要注明函数的域,   已经说明它定义出一个同名的函数了.  
   
   
  如果你害怕这种事情的发生,   可以强制使用    
  obj.rtti::getTypeName()   来调用.  
   
  但是意义明确的函数名,   是不应该发生混淆的.  
  如果发生这种事情,   无论多继承还是单继承都有可能发生.  
   
  对于第2个问题,   现在大多数编译器会   warning   的  
   
  Top

69 楼cloudwu(云风)回复于 2002-06-12 03:02:26 得分 0

clone   只是举个例子,   这里没办法临时写出很复杂的具体运用.  
   
  ok.   今天就到这里了   :)   很晚了这里,明天还要上班.  
   
  我在项目中用   MI   时间不久,   之前多年都是绕开   MI   的.  
  所以也不能立刻列举出太多的切身体会的例子.    
  不过是我读完了   D&E   关于   "MI   为什么存在"的章节后,改变了一些想法.  
   
  先了解再使用,   心里有数.   这样,   带来的问题都能避免的.  
  而编译器在它的层面做的优化一直都比人工的高的.  
  好比   C   去模拟   C++   的类实现,   永远只能把   this   当第一个参数压栈传递.  
  而   C++   编译器可以放在   ecx   里通过寄存器传递,   效率上就要高一分.  
  一些看起来很自然用   MI   的问题,   绕过   C++     的特性去换别的方式  
  解决.   减低了隐含在   C++   代码后面的东西的复杂度,   但可能牺牲了  
  编译器优化的余地.Top

70 楼anrxhzh(百宝箱)回复于 2002-06-12 10:22:22 得分 0

好夜战!  
   
  继承vs组合  
  这里的继承仅指对接口的继承,包括单继承和多继承,并且不考虑钻石问题,因为这只是多继承的实现细节。  
  继承相对于组合,直观,简单,静态。  
  组合相对于继承,不直观,复杂,动态。  
  由于组合的动态性和继承的被滥用,Design   Patterns   在引言中导出了这样的原则:优先使用组合,而不是类继承。但是考虑到继承的直观和简单,我并不认为组合能完全地代替继承。另外,在组合两个类型时,我们可能会很难决定以哪个类型为主。特别的,在C++中的类型系统中,多继承出来的新类型可以向上转换为任何一个超类型,这是组合所不具备的优势。Top

71 楼anrxhzh(百宝箱)回复于 2002-06-12 12:27:40 得分 10

to   :   huxw()  
                                                车             战             船  
                                                    \         /     \         /  
                                                      战车         战船  
                                                              \     /  
                                                          水陆坦克  
   
  在上面的例子中,最上层的车、战、船显然是三个理论上不相关的类型,所以第二层的设计者认为从这样的类型中多重继承是合理的。但是第三层的设计者就没那么幸运了,钻石是不可避免的。当然,如果是纯粹的接口继承,就可以不用考虑钻石问题。我要修正一下我的结论:钻石问题仅存在于实现继承中。  
   
  单根体系没有理论上的合理性,就不谈它了。  
  Top

相关问题

  • inheritance
  • damn MI (cont'd)
  • damn it DEV CPP!
  • Predefined class member functions and inheritance
  • 关于CEdit(multi line)
  • pool data in multi servers,need data consistent
  • 关于概念(concept)的改善(refinement)和类(class)的继承(inheritance):
  • 表格与a multi-select list box 的问题!!!
  • 请教2个关于multi的程序
  • Damn!!这是个什么世界,看看辽宁的高考狗B查分方法。

关键词

  • c++
  • 函数
  • 指针
  • virtual
  • 编译器
  • 接口
  • 继承
  • typeinfo
  • typeinfoimpl
  • rtti

得分解答快速导航

  • 帖主:ajoo
  • kof99th
  • huxw
  • cloudwu
  • prototype
  • cloudwu
  • prototype
  • prototype
  • huxw
  • anrxhzh
  • anrxhzh

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
惹火投票。。火热进行中...
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
CSDN网站24小时值班电话:13552009689
Copyright © 2000-2009, CSDN.NET, All Rights Reserved
GongshangLogo