我是一个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()函数不被覆盖?任何帮助,甚至是链接,都将不胜感激。
答案 0 :(得分:5)
只是做:
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用于解析函数地址。这是动态绑定在虚函数调用期间发生的方式。
这个想法是,编译器根据对象的内存地址存储指向每个方法的指针。它需要指针来访问表并调用适当的函数指针。想想你是否想编写一个OO版本的C,你会如何提供继承和多态这样的机制?当你这么想的时候,这是有道理的。
我刚刚读到你正在从JAVA转移。在JAVA中,大多数对象都存储在堆上。这一切都只是暗示。
JAVA的
Enemy n = new Ninja();
n.attack();
大致相当于
Enemy* n = new Ninja();
n->attack();
凡在哪里。 JAVA中的运算符更像是 - &gt; c ++中的运算符。在这两种情况下,n都在堆上。 Java只是隐藏了你的所有指针和内存管理。这就是为什么你可以对JAVA和C#中动态绑定的工作方式一无所知。
答案 1 :(得分:4)
这与您不通过指针访问敌人的事实有关:
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;
答案 2 :(得分:2)
除非您通过指针或引用访问对象,否则虚函数调用将不起作用。为了使它工作,你需要重新编写你的敌人阵列
Enemy *enemies[2];
enemies[0] = &m;
enemies[1] = &n;
请注意,您必须将所有enemies[i].
更改为enemies[i]->
。
答案 3 :(得分:1)
Enemy enemies[2];
enemies[0] = m;
enemies[1] = n;
这只是对象切片 - 只会复制派生对象中的Enemy
个对象。虚拟功能无法发挥作用。
答案 4 :(得分:0)
这种现象称为object slicing。