这里我要讨论的“虚”函数并不是指一般意义上的虚函数。因为虚函数能在不知其类型的情况下实现其多态特征。而这里所说的“虚”函数,只是一种compiler trick而以。
    在一般面向对象语言中,如需要让父类调用派生类的函数,一般是通过多态来实现,也有如使用代理或者成员函数指针来实现。但是多态实现,不仅增加空间上的开销还增加时间上的开销。而使用代理虽然更加灵活,但是带来的开销和维护更大。但是并不是所有的父类调用子类函数都需要以这两种方式进行,例如DirectX SDK的CD3DApplication这一类情况,是可以在明确知道其子类型的情况下进行的子类成员函数调用,不知道是哪个天才想到使用模版来实现,我也是前段时间看WTL才发现这一神奇的魔术例如:
template<typename T>
class CBase
{
public:
void DoFoo()
{
(reinterpret_cast<T*>(this))->Foo();
}
void Foo()
{
}
};
class CDerive:public CBase<CDerive>
{
public:
void Foo()
{
cout<<"CDerive::Foo"<<endl;
}
};
    这样带来的后果就是,基类成了无法实例化的了。但是却能方便调用派生类成员函数,再加上内联展开,能在一定程度上大大增加效率    而C#中实现这么一种方法,我给出的:
    interface IFoo
    {
        void Foo();
    }
    class CBase<T> where T : IFoo
    {
        public void DoFoo()
        {
            T f = (T)(object)this;
            f.Foo();
        }
        public void Foo()
        {
            
        }
    }
    class CDerive : CBase<CDerive>, IFoo
    {
        public new void Foo()
        {
            Console.WriteLine("CDerive::Foo");
        }
    }
    但是这样的做法,就必须存在一个IFoo接口,来说明这个T有个Foo方法,为什么?因为C#2的泛型特化不是在编译期,而是在运行期由JIT实现的,编译器不可能知道这里的T会成为CDerive,所以T没有Foo方法是对的,就应该用接口来约束Foo方法了。
    用接口来说明方法的存在带来的后果是,增加了空间的开销,因为这个CDerive必须维护一个有IFoo的接口表。而且通过反汇编分析,这样的做法减少了因为虚拟表的产生而导致的间接二次寻址,但是带来的问题是多了两次类型转换而产生的类型检查,这里却产生了效率开销,这个看起来比虚拟函数带来的执行开销更大
    请问各位高手,觉得怎么样处理这个问题比较好?减轻VMT负担的同时增加执行效率。
    这个问题只是在群里和别人聊天的时候想到的,呵呵,觉得好玩就发上来了,欢迎各位高手菜鸟拍砖

解决方案 »

  1.   


    已阅!这个问题的确存在,所以说C#是强类型,是安全代码(语言)我没有好的方法处理. up
      

  2.   


    http://blog.joycode.com/sunmast/archive/2005/12/16/csharp_generic_misleading.aspx事实上C# 2.0中的泛型限制较多,微软提供的解决方案也不尽如人意,有很多问题都没有得到解决,但不可否认能使其处于一种相对平衡的状态也不失为一件好事。我个人觉得那些所谓的方案基本上都是一样的,没有哪个能真正把所有问题都解决的,所以关键依然是看在实际开发的过程中开发人员如何去取舍。基本上就是这样了^_^
      

  3.   

    对于statically-typed的语言,这大概很难,但在VB 9里使用后期绑定的话,还是很有意思的,虽然后期绑定必然会带来你上面所说的运行时的开销,但有些东西是很难两全的http://blog.joycode.com/saucer/archive/2006/01/25/70841.aspx
      

  4.   

    "但是这样的做法,就必须存在一个IFoo接口,来说明这个T有个Foo方法,为什么?因为C#2的泛型特化不是在编译期,而是在运行期由JIT实现的,编译器不可能知道这里的T会成为CDerive,所以T没有Foo方法是对的,就应该用接口来约束Foo方法了。
        用接口来说明方法的存在带来的后果是,增加了空间的开销,因为这个CDerive必须维护一个有IFoo的接口表。而且通过反汇编分析,这样的做法减少了因为虚拟表的产生而导致的间接二次寻址,但是带来的问题是多了两次类型转换而产生的类型检查,这里却产生了效率开销,这个看起来比虚拟函数带来的执行开销更大"必须去花时间去考虑接口对系统造成的影响吗?
    相比起来我到是必须得花大时间琢磨目前项目中那个该死的资产编码规则,我看还是忘了他吧
    呵呵
    @.@||~
      

  5.   

    "真要考虑执行效率问题,我不会选C#"why?@.@||~
      

  6.   

    另外我那个代码中,调用T的foo方法并没有因为T支持IFoo接口而产生一般的接口带来的开销,因为JIT进行对T=CDerive的特化的时候,就直接让其CALL CDerive::Foo了,而且不是虚函数调用。这里的接口只是绕过编译器的提示
    我的代码中效率瓶颈在两次类型转换。
      

  7.   

    你不觉得两次类型转换本身就有点bad smell吗?@.@||~
      

  8.   

    >>"真要考虑执行效率问题,我不会选C#">>why?>>@.@||~
    按照不同的需求,我可以选择C/C++,Delphi甚至汇编。并不是天下只有C#的前面说的我这里的接口调用并没有产生接口带来的虚函数调用开销,是通过理论和反汇编得到的不过和一般的继承并覆盖父类虚函数相比,限制多了,多态式虚函数允许public/protected,而这种方式只能public,呵呵
      

  9.   

    对啊。我知道那个bad smell所以我才来找个位讨论怎么去掉这个bad smell,原文中说到的“但是带来的问题是多了两次类型转换而产生的类型检查,这里却产生了效率开销”
      

  10.   

    我首先看到这个帖子的反应是,在.NET做这种细节的性能优化可能没有意义。虚函数机制还是很快的,最起码相对JIT和GC来讲它的性能消耗基本上是可以忽略的不过既然你想用别的方法实现“虚函数”,第一段C++代码是很酷,但你可以看我的例子,不需要Generic帮助,也可实现。关键是new关键字的作用(实际上我认为这是new关键字带来的负面的作用)。 public interface IFoo
    {
    void Foo();
    } public class VFBase : IFoo
    {
    public void DoFoo()
    {
    (this as IFoo).Foo();
    } public void Foo()
    {
    }
    } public class VFDerive : VFBase, IFoo
    {
    public new void Foo()
    {
    Console.WriteLine("VFDerive::Foo");
    }
    }前面有人贴出了我的那个blog链接,那个不说明这个问题嗯.. 而且其内容备受争议,仅供参考:-)
      

  11.   

    呵呵,我是说像C++版本的代码那样酷,既不需要额外的VMT实现,效率还能优化的和内联相比。我前面的使用了接口就必然引入了额外的VMT。可是我在使用上并没通过那个接口的VMT进行的“虚”函数调用,如何能将这个额外的VMT省掉还能避免运行时类型转换。
    而且前面说过我纯属无聊的讨论:)
      

  12.   

    C++的模板当然灵活,但有一定不安全性,是"签名"约束
    C#或托管代码泛型必须基类+接口,没办法,这是规矩,C#泛型约束更多还有各位前辈,托管代码强调的都是"显示....."
    咋不见你们用Generic呢,看的人迷茫啊 ~~~~
    :)