• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
蓝色的大海
我很卑微.
博客园    首页    新随笔    联系   管理    订阅  订阅

虚函数

 

namespace Test

{

    class CA

    {

        public virtual void Foo()

        {

            Console.WriteLine("CA.Foo");

        }

    }

 

    class CB : CA

    {

        public override void Foo()

        {

            Console.WriteLine("CB.Foo");

        }

    }

 

    class Test

    {

        public static void InvokeFoo(CA ca)

        {

            ca.Foo();

        }

 

        public static void Main()

        {

            InvokeFoo(new CB());

        }

    }

}

 

 

这里InvokeFoo()方法的参数类型是CA,但是传给它的参数是new CB(),这是一个CA ca=new CB()的问题,ca的指向其实是堆上CB的对象。

输出结果:CB.Foo

 

另外一种情况:

去掉基类的virtual和派生类的override

 

namespace Test

{

    class CA

    {

        public void Foo()

        {

            Console.WriteLine("CA.Foo");

        }

    }

 

    class CB : CA

    {

        public void Foo()

        {

            Console.WriteLine("CB.Foo");

        }

    }

 

    class Test

    {

        public static void InvokeFoo(CA ca)

        {

            ca.Foo();

        }

 

        public static void Main()

        {

            InvokeFoo(new CB());

        }

    }

}

 

 

问题:

本以为输出结果:CB.Foo

但是输出结果为:CA.Foo

 

原因:

  于关键字virtual的存在,它会告诉编译器但前这个函数是运行时绑定的,所以编译的时候,编译器无法知道ca的真实类型,只有运行的时候,才根据真实的参数类型,

来判断ca的真实类型,来判断ca来调用哪个函数,因为派生类重写了基类函数,所以输出结果为CB.Foo。

 

  而没有关键字virtual则是编译时绑定,编译的时候已经知道ca的类型是CA,已经把ca.Foo绑定到CA.Foo.

 

 

第三种情况:

去掉派生类的override

  

 

namespace Test

{

    class CA

    {

        public virtual void Foo()

        {

            Console.WriteLine("CA.Foo");

        }

    }

 

    class CB : CA

    {

        public void Foo()

        {

            Console.WriteLine("CB.Foo");

        }

    }

 

    class Test

    {

        public static void InvokeFoo(CA ca)

        {

            ca.Foo();

        }

 

        public static void Main()

        {

            InvokeFoo(new CB());

        }

    }

}

 

 

输出结果为:CA.Foo

 

按照上面的说法,ca是运行时绑定,派生类中并没有重写的方法,所以得到的还是基类的方法实现

 系统使用了一个叫做“虚函数指针表”的东西来使得程序在运行时可以调用到正确的方法,任何一个包含有虚方法的类型都对应有一个虚函数指针表。因为object类本身就定义了虚方法,而C#所有的类型都必须继承自object,因此所有的类都会有一个虚方法指针表。这个虚函数指针表中,保存了每个虚函数的签名和他的入口地址的对应关系。如果一个类型重写了基类的某个虚方法,那么就会在他的虚函数指针表中改写方法的入口地址,否则就把基类中这个虚函数的入口地址复制过来。

虚方法都是拷贝到子类方法表里面的,如果覆写,则覆盖继承的虚方法.由于是运行是邦定,调用的是父类还是派生类方法就要看引用了.

posted @ 2008-09-23 10:36  blue th  阅读(417)  评论(2)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3