‫所以在这节课上, 我们将讨论C++中的虚方法｡
‫我们以前见过它, 我们用过它, 但我们完全理解它了吗？
‫让我想想｡
‫所以虚方法的思想是它允许我们重写基类中的函数｡
‫我们已经在虚幻中看到了像开始､ 播放和抽搐这样的东西, 但是让我们进一步了解一下为什么它们被标记为虚拟的｡
‫它允许我们改变基类中的功能, 它做了一个叫做动态调度的事情, 我们稍后会讨论这个, 来理解它的真正含义,
‫以及为什么虚方法在C++中不是默认功能,
‫就像在很多其他语言中一样, 比如C Sharp.
‫让我们来看看这个｡
‫我会在CPP. S-H网站上做这个, 这样我们就不会在这里做虚幻了｡
‫我们只是看一下C++的原始功能, 这样你就可以理解这个语言特性以及它是如何工作的｡
‫现在, 我要做的第一件事就是清除main方法｡
‫在我们的用例中, 我们将考虑一个枪支类, 因为这是一个非常常见的游戏用例｡
‫让我们来看看一个叫gun的类, 它的类体是public的,
‫还有一个void的shoot方法.
‫我将在这里声明shoot方法的主体以使事情变得非常简单｡
‫顺便说一句, 如果你以前没见过, 你也可以这么做, 但一般来说,
‫我们希望在一个单独的C++文件中做一些事情, 以保持独立, 使编译时间更快, 等等｡
‫但这里只是一个玩具的例子, 这是好的, 所以我们要做一个STD C｡
‫所以用C++作为输出字符串的东西, 当我们调用这个方法的时候, 我们会发出砰的一声, 然后我们会结束这一行｡
‫而这就是他们对我们的身体射击的方法｡
‫我们可以用花括号和分号来结束这个类｡
‫所以我们来使用这个类｡
‫我将在main中创建一个my gun变量, 并调用my gun dot shoot,
‫让我们看看这里的一切是否正常.
‫按下运行按钮然后砰｡
‫正如我们所期望的那样, 它正在调用方法｡
‫现在, 如果我想延长我的枪, 也许使步枪或手枪会发生什么？
‫就叫它手枪吧｡
‫所以类手枪是一种从枪上继承下来的东西, 哪会这样子做｡
‫如果你以前没看过因为你可能没写过, 你应该做一个从guns的公共继承这样其他类就能看到你是从guns继承的.
‫然后我们在公共部分, 我们想为手枪创建自己的射击方法来覆盖这个功能｡
‫我们不想在结尾处印“砰”的一声, 而是印“丸”的一声, 因为这是一把更小的枪, 它发出的声音更小,
‫这就是我们的射击方法｡
‫让我们结束这堂课吧｡
‫我们要做的就是给自己造一把手枪｡
‫所以手枪我的手枪在这里的主要方法｡
‫因为它们只存在于main方法中, 所以我们不使用指向它们的指针.
‫这就是为什么我们在这里使用了shot操作符而不是箭头｡
‫所以我的手枪没有开枪｡
‫让我们看看它有什么用｡
‫我希望它能打印PPU, 它确实做到了｡
‫好的, 谢谢
‫好的, 我们先移除呼叫的方法｡
‫直接拍摄这些, 让我们看看如何与指针的事情会表现｡
‫所以我将创建一个名为Gun的枪指针｡
‫彼得彼得
‫在下一行我可以在同一行上做｡
‫但为了简单起见, 我将在下一行中执行此操作｡
‫我将把枪指针设置为指向的地址, 这是操作符的地址, 我的枪变量的地址｡
‫因为这是一个指针, 我们需要使用箭头操作符来调用shoot方法.
‫我用我的枪跟注shoot, 然后单击run, 结果是bang｡
‫好的, 谢谢
‫如果我做同样的事会怎样？
‫但是我没有把枪指针设置成指向我的枪, 而是把它设置成指向我的手枪｡
‫我们现在就按“运行”｡
‫好吧, 我会的
‫这是一个有趣的行为｡
‫这里的情况是它在两个实例中都调用了第一个方法｡
‫那么, 为什么没有调用第二个shoot方法呢？
‫我们这里有把手枪作为指示器｡
‫如果你没有虚方法的话, C++会默认使用指针的类型, 这是一个枪指针.
‫请记住, 它将使用该类型来查找要调用的方法｡
‫所以它会在顶部调用这个shoot方法, 而不是pistle中被覆盖的方法｡
‫若要让它呼叫Pistle中覆写的和方法, 它必须呼叫, 而且必须有Pistle型别的指标｡
‫现在这很好｡
‫你可以看到他们为什么这样做｡
‫这是为了提高效率｡
‫C++的核心就是效率｡
‫如果某个东西在编译和运行时会花费更多的成本, 那么C++在默认情况下不会这样做, 除非你要求它这样做｡
‫所以C++很容易就能查到, 这是一个枪指针, 我把它叫做枪指针方法.
‫这就是动态调度的用武之地｡
‫这个花哨的术语基本上意味着我们不希望它这样做｡
‫我们想让它说, 等一下, 枪的指针里到底储存了什么？
‫真实的的类型是什么？
‫我们意识到它的真实的类型是一把手枪｡
‫所以我们要问自己, 是否有一个方法, 用于被覆盖的类型？
‫是的, 有｡
‫那么我们如何给予C++权限, 让它的效率稍微低一点, 并让我们能够重写,
‫而不必改变指针类型, 这并不总是可能的｡
‫事实上, 在许多情况下这是不可能的, 当您拥有自己的自定义actor类时就是这种情况｡
‫Unreal没有设置指针指向你的特定act类, 这将要求他们在每次你引入一个新类时改变整个引擎｡
‫不, 这不可能｡
‫他们有一个指向演员星星类型的指针｡
‫所以当它们调用开始play时, 你希望它调用你的begin play方法｡
‫所以它必须是虚拟的｡
‫这就是关键所在｡
‫如果我们在父类的方法前面加上virtual, 这会通知C++挂起一秒钟, 如果我在一个gun指针类型的指针上调用shoot,
‫我需要检查它是否是, 比如说, 手枪,
‫并且这个shoot方法已经被覆盖了.
‫如果我们现在继续按run, 您可以看到我们得到bang, 然后pill｡
‫因此, 它的工作正如我们对动态调度的预期｡
‫这是这个东西的术语, 我们的意思是检查指针中到底是什么类型的, 然后调用相关的方法.
‫现在有一个额外的语法, 你会经常看到使用, 那就是override关键字｡
‫你把它放在一个你试图重写为虚拟的方法之后｡
‫基本上, override关键字并不是绝对必要的, 也不是把virtual再次放在gun的子类上｡
‫所以你不必装虚, 但做起来也无妨｡
‫但是override特别有用, 因为它允许我们确保我们的命名是正确的｡
‫所以如果我点击运行, 一切都像以前一样运行, 当我有覆盖｡
‫但是假设我把这个方法的名字弄错了, 所以我不做覆盖｡
‫我说这是不可能的｡
‫比如, 我把名字抄错了｡
‫我把它打了进去, 而不是复印｡
‫如果我现在按run, 那么你会看到, 我们又遇到了同样的错误, 因为我们试图跟注投牌｡
‫我们错误地覆盖了它｡
‫砰, 砰｡
‫所以我可以避免这种情况的方法是说, 我正在尝试覆盖一些东西｡
‫所以我在这里放了override关键字, 如果我现在编译它, 你会看到shooter被标记为override, 但是没有方法, 没有虚拟方法可以重写,
‫在这种情况下, 我可以说, 哦, 是的,
‫我打错了.
‫所以我将继续更正我的代码, 然后让它返回拍摄｡
‫而现在它正如我所想的那样工作｡
‫所以把这个超驰装置放在那里, 就有了一点额外的安全性｡
‫它还可以防止我们试图覆盖的东西不是虚拟的｡
‫如果我尝试这样做, 你可以看到它说它被标记为重写, 但它没有重写,
‫因为那个父类没有shoot的虚函数｡
‫所以希望这已经给了你们一些关于C++如何使用和实现虚函数的概述, 以及为什么我们必须对它们如此明确｡
‫现在, 让我们考虑一下一般的虚函数的更多用例｡
‫我们已经从引擎上看到了一些｡
‫您以前在哪里见过虚拟应用？
‫你能想象在哪里使用它吗？
‫我们刚刚看到了一个用例示例, 其中您可能有一把枪, 以及以不同方式实现射击的不同覆盖｡
‫当您想到这两个问题的答案时, 您还能想到使用覆盖父类中的功能的虚函数吗？
‫点击链接, 进入本课程的讨论, 并在那里发表你的答案｡
‫使用案例和您以前使用过它们的地方, 看看其他人提出了什么｡
‫也许这会帮助你巩固你对虚拟方法的理解｡
‫这节课到此为止｡
‫下一节我们将在愤怒中使用虚方法｡