对C++的虚拟函数感到困惑

Confused about Virtual Functions C++

本文关键字:函数 C++ 虚拟      更新时间:2023-10-16

我是一个c++n00b,我不确定我是否找对了地方,但我对此感到困惑:

include <iostream>
using namespace std;
class Enemy
{
    public:
        void sayHere()
        {
            cout<<"Here"<<endl;
        }
        virtual void attack()
        {
        }
};
class Monster: public Enemy
{
    public:
        virtual void attack()
        {
            cout<<"RAWR"<<endl;
        }
};
class Ninja: public Enemy
{
    public:
        virtual void attack()
        {
            cout<<"Hiya!"<<endl;
        }
};
int main()
{
    Ninja n;
    Monster m;
    Enemy enemies[2];
    enemies[0] = m;
    enemies[1] = n;
    for(int i = 0; i<2; i++)
    {
        enemies[i].sayHere();
        enemies[i].attack();//Will not be overriden
    }
    return 0;
}

我的问题是为什么Monster或Ninja类中的attack((函数没有被覆盖?任何帮助,哪怕是一个链接,都将不胜感激。

只需执行:

Enemy* n = new Ninja();
Enemy* m = new Monster();
n->sayHere();
n->attack();
m->sayHere();
m->attack();
delete n;
delete m;

你想怎么做就怎么做。你需要使用指针来使它工作。原因在于动态绑定的工作方式。

每当程序声明了C++虚拟函数时,就会为该类构造一个v表。v表由指向类的虚拟函数的地址和指向派生类的每个对象的函数的指针组成。每当对c++虚拟函数进行函数调用时,都会使用v-table来解析函数地址。这就是在虚拟函数调用期间动态绑定的发生方式。

其思想是,编译器根据对象的内存地址存储指向每个方法的指针。它需要指针来访问表并调用相应的函数指针。想一想,如果你想编写一个面向对象的C版本,你将如何提供继承和多态性这样的机制?当你这样想的时候,这是有道理的。

我刚刚读到你要离开JAVA。在JAVA中,大多数对象都存储在堆中。这一切都只是暗示。

JAVA的

Enemy n = new Ninja();
n.attack();

大致相当于

Enemy* n = new Ninja();
n->attack();

在哪里。JAVA中的运算符更像c++中的->运算符。在这两种情况下,n都在堆上。Java只是对您隐藏了所有指针和内存管理的东西。这就是为什么您可能对JAVA和C#中动态绑定的工作方式一无所知。

这与你不能通过指针访问敌人有关:

    Ninja n;
    Monster m;
    Enemy *enemies[2];
    enemies[0] = &m;
    enemies[1] = &n;
    for (int i = 0; i < 2; i++)
    {
        enemies[i]->sayHere();
        enemies[i]->attack();
    }
    return 0;

除非通过指针或引用访问对象,否则虚拟函数调用将无法工作。为了让它发挥作用,你需要将你的敌人阵列重写为

Enemy *enemies[2];
enemies[0] = &m;
enemies[1] = &n;

请注意,您必须将所有enemies[i].更改为enemies[i]->

Enemy enemies[2];   
enemies[0] = m;    
enemies[1] = n;

这只是对象切片——只会复制派生对象中的Enemy对象。虚拟功能无法发挥作用。

这种现象称为对象切片。