抽象函数和虚函数有什么区别?

Mor*_*man 1526 oop programming-languages virtual-functions abstract

抽象函数和虚函数有什么区别?在哪些情况下建议使用虚拟或抽象?哪一个是最好的方法?

BFr*_*ree 2655

抽象函数不具备功能.你基本上是在说,任何一个子类必须给出他们自己的这个方法的版本,但是它太普遍甚至不能尝试在父类中实现.

一个虚函数,基本上就是说看,这里的功能对于子类来说可能是也可能不够好.因此,如果它足够好,请使用此方法,如果没有,则覆盖我,并提供您自己的功能.

  • 当然,如果你覆盖一个虚方法,你总是可以通过调用base.Foo(...)来引用父方法. (385认同)
  • 谢谢.这是一个比MSDN文档中的任何内容都要好得多且简单易懂的解释.(阅读五分钟之后我很头疼:http://msdn.microsoft.com/en-us/library/aa645767(v = vs.71) )的.aspx) (194认同)
  • 这应该在Microsoft参考库中,我花了10分钟阅读并仍然感到困惑. (16认同)
  • 来自Java,我有点困惑为什么我们需要将它变为虚拟,直到我读到这个:http://stackoverflow.com/a/1062126/193634 (15认同)
  • @MeqDotNet这意味着如果你喜欢我的实现,请使用我,如果不比我自己写得更好:) (4认同)
  • 可能值得一提的是,Jake上面提到的关于**虚拟**的令人头疼的MSDN文档来自2003年的文档.以下来自2013年文档的MSDN链接更加简洁:http://msdn.microsoft.com/en-us/library/9fkccyh4.aspx (4认同)
  • 您还需要将父类标记为抽象类,以强制子类实现所有抽象方法/函数 (2认同)

Jos*_*rke 289

抽象函数没有实现,只能在抽象类上声明.这会强制派生类提供实现.虚函数提供默认实现,它可以存在于抽象类或非抽象类中.例如:

public abstract class myBase
{
    //If you derive from this class you must implement this method. notice we have no method body here either
    public abstract void YouMustImplement();

    //If you derive from this class you can change the behavior but are not required to
    public virtual void YouCanOverride()
    { 
    }
}

public class MyBase
{
   //This will not compile because you cannot have an abstract method in a non-abstract class
    public abstract void YouMustImplement();
}
Run Code Online (Sandbox Code Playgroud)

  • 查看示例代码非常有用 - 有助于使答案中的各种解释更加清晰. (27认同)
  • 我回滚了以前版本的答案:这两个类只是示例,第一个类将编译,因为它被标记为抽象,第二个不会.MyBase是否从其他类继承是无关紧要的. (2认同)
  • 你的`MyBase`类不是必须以某种方式实现**抽象**类吗?我不经常这样做,所以我可能会弄错.在你的例子中我没有看到. (2认同)
  • 在上面的例子中,MyBase显示了你不能做的事情.也就是说,你不能在非抽象类中使用抽象方法 (2认同)

Meh*_*ari 78

  1. 只有abstract班级才能拥有abstract会员.
  2. 一个非abstract类从继承abstract必须 overrideabstract成员.
  3. 一个abstract成员是隐式virtual.
  4. 一个abstract成员不能提供任何实现(abstract被称为pure virtual在某些语言).

  • 不,我的意思正是我写的.抽象类的成员可以是"虚拟"或非"虚拟".一个`abstract`成员(即抽象属性,抽象方法)就像一个虚方法,即你可以覆盖它,除了它不带有默认实现. (5认同)

Rin*_*lin 61

您必须始终覆盖抽象函数.

从而:

  • 抽象函数 - 当继承者必须提供自己的实现时
  • 虚拟 - 何时由继承人决定


小智 35

摘要功能:

  1. 它只能在抽象类中声明.
  2. 它只包含方法声明而不是抽象类中的实现.
  3. 必须在派生类中重写它.

虚函数:

  1. 它可以在abstract和non abstract类中声明.
  2. 它包含方法实现.
  3. 它可能被覆盖了.


Fre*_*els 29

抽象方法:当一个类包含一个抽象方法时,该类必须声明为抽象.抽象方法没有实现,因此,从该抽象类派生的类必须为此抽象方法提供实现.

虚方法:类可以有一个虚方法.虚方法有一个实现.从具有虚方法的类继承时,可以覆盖虚方法并提供其他逻辑,或者将逻辑替换为您自己的实现.

何时使用:在某些情况下,您知道某些类型应该具有特定方法,但是,您不知道此方法应该具有什么实现.
在这种情况下,您可以创建一个包含具有此签名的方法的接口.但是,如果您有这种情况,但是您知道该接口的实现者还将有另一个常用方法(您已经可以为其提供实现),则可以创建一个抽象类.然后,此抽象类包含抽象方法(必须覆盖),以及包含"公共"逻辑的另一种方法.

如果您有一个可以直接使用的类,但是您希望继承者能够更改某些行为,但是它不是必需的,则应该使用虚方法.


BKS*_*eon 28

解释:用类比.希望它会对你有所帮助.

上下文

我在一栋建筑的21楼工作.我对火很偏执.在世界的某个地方,每时每刻都有一场大火烧毁着天空刮板.但幸运的是,我们在这里有一个说明手册,如果发生火灾该怎么办:

火灾逃生()

  1. 不要收集物品
  2. 走到火灾逃生
  3. 走出建筑物

这基本上是一个名为FireEscape()的虚方法

虚方法

对于99%的情况,这个计划非常好.这是一个有效的基本计划.但是火灾逃生被堵塞或损坏的可能性有1%,在这种情况下,你完全被拧紧,除非你采取一些激烈的行动,否则你会变成烤面包.使用虚拟方法,您可以这样做:您可以使用您自己的计划版本覆盖基本的FireEscape()计划:

  1. 运行到窗口
  2. 跳出窗外
  3. 降落伞安全到底

换句话说,虚拟方法提供了一个基本计划,如果需要,可以覆盖它.如果程序员认为合适,子类可以覆盖父类的虚方法.

抽象方法

并非所有组织都经过精心培训.有些组织不进行消防演习.他们没有全面的逃避政策.每个人都是为了自己.管理层只对现有的此类政策感兴趣.

换句话说,每个人都被迫开发自己的FireEscape()方法.一个人会走出火灾逃生.另一个人会降落伞.另一个人将使用火箭推进技术飞离建筑物.另一个人会下降.管理层并不关心你如何逃脱,只要你有一个基本的FireEscape()计划 - 如果他们不这样做,你可以保证OHS会像一吨砖一样落在组织上.这就是抽象方法的含义.

两者之间有什么区别?

抽象方法:子类被迫实现自己的FireEscape方法.使用虚拟方法,您有一个等待您的基本计划,但如果不够好,可以选择实施自己的计划.

现在那不是很难吗?


Kam*_*oij 22

抽象方法是必须实现以创建具体类的方法.声明在抽象类中(并且任何具有抽象方法的类必须是抽象类)并且必须在具体类中实现.

虚方法是一种方法,可以使用覆盖在派生类中重写,替换超类中的行为.如果不覆盖,则会获得原始行为.如果你这样做,你总会得到新的行为.这与非虚拟方法相反,不能覆盖但可以隐藏原始方法.这是使用new修饰符完成的.

请参阅以下示例:

public class BaseClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }


    public virtual void SayGoodbye()
    {
        Console.WriteLine("Goodbye");
    }

    public void HelloGoodbye()
    {
        this.SayHello();
        this.SayGoodbye();
    }
}


public class DerivedClass : BaseClass
{
    public new void SayHello()
    {
        Console.WriteLine("Hi There");
    }


    public override void SayGoodbye()
    {
        Console.WriteLine("See you later");
    }
}
Run Code Online (Sandbox Code Playgroud)

当我实例化DerivedClass并打电话时SayHello,或者SayGoodbye,我得到"Hi There"和"See you later".如果我打电话HelloGoodbye,我会得到"你好"和"以后见".这是因为SayGoodbye是虚拟的,可以用派生类替换.SayHello只是隐藏,所以当我从我的基类调用它时,我得到了我原来的方法.

抽象方法是隐式虚拟的.它们定义必须存在的行为,更像界面.


Ras*_*ack 10

抽象方法总是虚拟的.他们无法实施.

这是主要的区别.

基本上,如果您具有虚拟方法,并且希望允许后代更改其行为,则可以使用虚方法.

使用抽象方法,可以强制后代提供实现.


Meq*_*Net 10

我通过对以下类(从其他答案)进行一些改进使这更简单:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestOO
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClass _base = new BaseClass();
            Console.WriteLine("Calling virtual method directly");
            _base.SayHello();
            Console.WriteLine("Calling single method directly");
            _base.SayGoodbye();

            DerivedClass _derived = new DerivedClass();
            Console.WriteLine("Calling new method from derived class");
            _derived.SayHello();
            Console.WriteLine("Calling overrided method from derived class");
            _derived.SayGoodbye();

            DerivedClass2 _derived2 = new DerivedClass2();
            Console.WriteLine("Calling new method from derived2 class");
            _derived2.SayHello();
            Console.WriteLine("Calling overrided method from derived2 class");
            _derived2.SayGoodbye();
            Console.ReadLine();
        }
    }


    public class BaseClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello\n");
        }
        public virtual void SayGoodbye()
        {
            Console.WriteLine("Goodbye\n");
        }

        public void HelloGoodbye()
        {
            this.SayHello();
            this.SayGoodbye();
        }
    }


    public abstract class AbstractClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello\n");
        }


        //public virtual void SayGoodbye()
        //{
        //    Console.WriteLine("Goodbye\n");
        //}
        public abstract void SayGoodbye();
    }


    public class DerivedClass : BaseClass
    {
        public new void SayHello()
        {
            Console.WriteLine("Hi There");
        }

        public override void SayGoodbye()
        {
            Console.WriteLine("See you later");
        }
    }

    public class DerivedClass2 : AbstractClass
    {
        public new void SayHello()
        {
            Console.WriteLine("Hi There");
        }
        // We should use the override keyword with abstract types
        //public new void SayGoodbye()
        //{
        //    Console.WriteLine("See you later2");
        //}
        public override void SayGoodbye()
        {
            Console.WriteLine("See you later");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Rod*_*man 6

绑定是将名称映射到代码单元的过程.

后期绑定意味着我们使用名称,但推迟映射.换句话说,我们首先创建/提及名称,然后让一些后续进程处理代码到该名称的映射.

现在考虑:

  • 与人类相比,机器非常擅长搜索和排序
  • 与机器相比,人类在发明和创新方面非常擅长

所以,简短的回答是:virtual是机器的后期绑定指令(运行时),而是abstract人类(程序员)的后期绑定指令

换句话说,virtual意思是:

"亲爱的运行时,通过做你最擅长的事情,将适当的代码绑定到这个名称:搜索 "

鉴于abstract手段:

"亲爱的程序员,请通过做你最擅长的事情将适当的代码绑定到这个名称:发明 "

为了完整起见,重载意味着:

"亲爱的编译器,通过做你最擅长的事情将相应的代码绑定到这个名称:排序 ".