抽象函数和虚函数有什么区别?在哪些情况下建议使用虚拟或抽象?哪一个是最好的方法?
抽象函数不具备功能.你基本上是在说,任何一个子类必须给出他们自己的这个方法的版本,但是它太普遍甚至不能尝试在父类中实现.
一个虚函数,基本上就是说看,这里的功能对于子类来说可能是也可能不够好.因此,如果它足够好,请使用此方法,如果没有,则覆盖我,并提供您自己的功能.
抽象函数没有实现,只能在抽象类上声明.这会强制派生类提供实现.虚函数提供默认实现,它可以存在于抽象类或非抽象类中.例如:
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();
}
只有abstract
班级才能拥有abstract
会员.
一个非abstract
类从继承abstract
类必须 override
的abstract
成员.
一个abstract
成员是隐式virtual
.
一个abstract
成员不能提供任何实现(abstract
被称为pure virtual
在某些语言).
您必须始终覆盖抽象函数.
从而:
抽象函数 - 当继承者必须提供自己的实现时
虚拟 - 何时由继承人决定
摘要功能:
它只能在抽象类中声明.
它只包含方法声明而不是抽象类中的实现.
必须在派生类中重写它.
虚函数:
它可以在abstract和non abstract类中声明.
它包含方法实现.
它可能被覆盖了.
抽象方法:当一个类包含一个抽象方法时,该类必须声明为抽象.抽象方法没有实现,因此,从该抽象类派生的类必须为此抽象方法提供实现.
虚方法:类可以有一个虚方法.虚方法有一个实现.从具有虚方法的类继承时,可以覆盖虚方法并提供其他逻辑,或者将逻辑替换为您自己的实现.
何时使用:在某些情况下,您知道某些类型应该具有特定方法,但是,您不知道此方法应该具有什么实现.
在这种情况下,您可以创建一个包含具有此签名的方法的接口.但是,如果您有这种情况,但是您知道该接口的实现者还将有另一个常用方法(您已经可以为其提供实现),则可以创建一个抽象类.然后,此抽象类包含抽象方法(必须覆盖),以及包含"公共"逻辑的另一种方法.
如果您有一个可以直接使用的类,但是您希望继承者能够更改某些行为,但是它不是必需的,则应该使用虚方法.
解释:用类比.希望它会对你有所帮助.
上下文
我在一栋建筑的21楼工作.我对火很偏执.在世界的某个地方,每时每刻都有一场大火烧毁着天空刮板.但幸运的是,我们在这里有一个说明手册,如果发生火灾该怎么办:
火灾逃生()
不要收集物品
走到火灾逃生
走出建筑物
这基本上是一个名为FireEscape()的虚方法
虚方法
对于99%的情况,这个计划非常好.这是一个有效的基本计划.但是火灾逃生被堵塞或损坏的可能性有1%,在这种情况下,你完全被拧紧,除非你采取一些激烈的行动,否则你会变成烤面包.使用虚拟方法,您可以这样做:您可以使用您自己的计划版本覆盖基本的FireEscape()计划:
运行到窗口
跳出窗外
降落伞安全到底
换句话说,虚拟方法提供了一个基本计划,如果需要,可以覆盖它.如果程序员认为合适,子类可以覆盖父类的虚方法.
抽象方法
并非所有组织都经过精心培训.有些组织不进行消防演习.他们没有全面的逃避政策.每个人都是为了自己.管理层只对现有的此类政策感兴趣.
换句话说,每个人都被迫开发自己的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
并打电话时SayHello
,或者SayGoodbye
,我得到"Hi There"和"See you later".如果我打电话HelloGoodbye
,我会得到"你好"和"以后见".这是因为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"); } } }
绑定是将名称映射到代码单元的过程.
后期绑定意味着我们使用名称,但推迟映射.换句话说,我们首先创建/提及名称,然后让一些后续进程处理代码到该名称的映射.
现在考虑:
与人类相比,机器非常擅长搜索和排序
与机器相比,人类在发明和创新方面非常擅长
所以,简短的回答是:virtual
是机器的后期绑定指令(运行时),而是abstract
人类(程序员)的后期绑定指令
换句话说,virtual
意思是:
"亲爱的运行时,通过做你最擅长的事情,将适当的代码绑定到这个名称:搜索 "
鉴于abstract
手段:
"亲爱的程序员,请通过做你最擅长的事情将适当的代码绑定到这个名称:发明 "
为了完整起见,重载意味着:
"亲爱的编译器,通过做你最擅长的事情将相应的代码绑定到这个名称:排序 ".