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

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

答案

抽象函数不能具有功能。您基本上是在说,任何子类都必须提供自己的该方法的版本,但是它太笼统了,甚至无法尝试在父类中实现。

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

抽象函数没有实现,只能在抽象类上声明。这迫使派生类提供实现。

虚函数提供了默认实现,它可以存在于抽象类或非抽象类上。

因此,例如:

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();
}
  1. 只有abstract类可以具有abstract成员。
  2. abstract类继承的非abstract必须 overrideabstract成员。
  3. abstract成员是隐式virtual
  4. abstract成员不能提供任何实现( abstract在某些语言中称为pure virtual )。

您必须始终重写抽象函数。

从而:

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

抽象功能:

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

虚函数:

  1. 它可以在抽象类以及非抽象类中声明。
  2. 它包含方法的实现。
  3. 它可能会被覆盖。

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

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

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

如果您有一个可以直接使用的类,但是您希望继承者能够更改某些行为,尽管不是强制性的,则应使用虚拟方法。

说明:类推。希望它将对您有所帮助。

语境

我在建筑物的 21 楼工作。我对火灾抱有偏执。时不时地,在世界某个地方,大火烧毁了刮板。但幸运的是,在这里,我们有一份说明手册,介绍发生火灾时的处理方法:

火灾逃生()

  1. 不要收拾财物
  2. 步行逃生
  3. 走出大楼

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

虚方法

该计划在 99%的情况下都非常好。这是可行的基本计划。但是,只有 1%的可能性防火通道会被阻塞或损坏,在这种情况下,您将完全被拧紧,除非采取严厉措施,否则您将变成烤面包。使用虚方法,您可以做到这一点:您可以使用自己的计划版本覆盖基本的 FireEscape()计划:

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

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

抽象方法

并非所有组织都进行了深入的研究。一些组织不进行消防演习。他们没有整体的逃生政策。每个人都是他自己。管理层仅对现有这样的政策感兴趣。

换句话说,每个人被迫发展自己的 FireEscape()方法。一个人会走出防火梯。另一个家伙会降落伞。另一个家伙将使用火箭推进技术从建筑物上飞走。另一个家伙会逃走。只要您有基本的 FireEscape()计划,管理人员就不会在意您如何逃生 - 如果没有,您可以保证 OHS 会像一吨砖一样落在组织上。这就是抽象方法的含义。

两者又有什么区别?

抽象方法:子类被强制实现自己的 FireEscape 方法。使用虚拟方法时,您有一个基本计划正在等待您,但是如果还不够好的话,可以选择实施自己的计划。

现在不是那么难吗?

抽象方法是必须实现以构成具体类的方法。声明在抽象类中(任何具有抽象方法的类都必须是抽象类),并且必须在具体类中实现。

虚方法是一种可以使用覆盖在派生类中覆盖, 替换超类中行为的方法。如果不覆盖,则会得到原始行为。如果这样做,您总是会得到新的行为。这与非虚拟方法相反,后者不能被覆盖但可以隐藏原始方法。这是使用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");
    }
}

当我实例化DerivedClass并调用SayHelloSayGoodbye ,我得到 “Hi There” 和 “稍后见”。如果我致电HelloGoodbyeHelloGoodbye收到 “Hello” 和 “稍后见”。这是因为SayGoodbye是虚拟的,可以用派生类代替。 SayHello仅被隐藏,因此当我从基类中调用它时,便得到了原始方法。

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

抽象方法始终是虚拟的。他们无法实现。

那是主要的区别。

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

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

通过对以下类进行一些改进(通过其他答案),我使此过程变得更简单:

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");
        }
    }
}