这个C ++代码将如何工作?

时间:2013-12-27 07:35:51

标签: c++ inheritance virtual-functions

以下是一些代码:

======================
[Player.cpp]
======================
#include "TmTeam.h"
#include "TmPlayer.h"
#include "Player.h"

void Player::doTurn()
{
    (...)
    Tm_doPost();
    (...)
} 

===================
[Player.h]
===================
class Player
{
    (...)
public:
    (...)
    virtual void Tm_doPost() = 0;
    (...)
}; 

===================
[TmPlayer.cpp]
===================
#include "TmPlayer.h"
#include "TmTeam.h"

void TmPlayer::Tm_doPost()
{
    (...)
} 

===================
[TmPlayer.h]
===================
#include "Player.h"
class TmPlayer : public Player
{
public:
    (...)
    void Tm_doPost();
    (...)
}; 

===================
[Team.cpp]
===================
#include "TmTeam.h"
#include "TmPlayer.h"
#include "Team.h"

void Team::doTurn()
{
    (...)
    Tm_doPost();
} 

===================
[Team.h]
===================
class Team
{
    (...)
public:
    (...)
    virtual void Tm_doPost() = 0;
    (...)
}; 

===================
[TmTeam.cpp]
=================== 
#include "TmTeam.h"
#include "TmPlayer.h"

void TmTeam::Tm_doPost()
{
    (...)
} 

===================
[TmTeam.h]
===================
#include "Team.h"
class TmTeam : public Team
{
    (...)
public:
    (...)
    void Tm_doPost();
    (...)
}; 

我想知道Tm_doPost()的哪个实现将在这里显示的两个调用中执行...我假设Team.cpp中的那个将使用TmTeam.cpp提供的定义(请纠正我,如果我“我错了”,但是在Player.cpp中调用了怎么办?它会使用TmPlayer.cpp中的定义,还是像Player.h中的那样返回0?在Player.h和Team.h中,关键字virtual有什么作用?

顺便说一下,我尝试尽可能准确地使用每个代码块顶部的包含,以防它与问题相关......

3 个答案:

答案 0 :(得分:1)

我会尽力回答你的问题,所以要明确我引用你回答的部分问题。

  

你的问题1:我想知道Tm_doPost()的哪个实现将在这里显示的两个调用中执行...我假设Team.cpp中的那个将使用TmTeam.cpp提供的定义(如果我错了,请纠正我),

我认为你的措辞令人误解。你知道,你不能创建一个" Team" 对象的实例,因为它没有实现虚拟功能" Tm_doPost()& #34; 即可。如果您查看 Team.cpp 文件,它只会实现 Team :: doTurn()

但是,班级" 团队" 子类 " TmTeam" 实现虚函数" Tm_doPost()" ,因此可以实例化。

您可以列出"团队" s,其中包含许多" TmTeam" 实例。实际上,任何超类都可以用作子类的总体类名。它被称为" 多态。" 例如:

List<Team> myList = new List<Team>();
myList.push_back(new TmTeam());
myList.push_back(new TmTeam());
myList.push_back(new TmTeam2()); // <--- if you have a class like this:  " class TmTeam2 : Team"
//Notice there's mixed items on a list of "Team"s, because each item shares the same parent class.

现在,如果我们执行此代码:

 //pseudocode, iterating through each item in myList, giving it the variable name "x"
for (each Team, x, in myList)
{
    x.Tm_doPost();
}

将执行Tm_doPost()的哪个实现?它会是TmTeam.cpp中的那个吗?不必要。 myList中的TmTeam对象将使用该函数,但TmTeam2对象将使用定义。您可以拥有许多子类,每个子类必须实现虚函数才能实例化。只要它在Team对象列表中,编译器就会想:&#39;我需要调用&#34; Tm_doPost()&#34;但我不确定这是哪个子类。它是TmTeam还是TmTeam2?&#39;但是,它并不需要怀疑,因为C ++的设计是为了找出对象真正的对象,无论是TmTeam还是TmTeam2。然后,它调用TmTeam对象上的函数,该对象自然地在其自己的类定义中使用该函数的定义。 TmTeam2将使用定义。就像正常的C ++行为一样,预计会有效。

所以,因为在Team.cpp中,&#34; Tm_doPost()&#34;是虚拟的,它必须找出实际的(可实例化的)对象是什么,然后调用它的实现&#34; Tm_doPost()。&#34;

  

你的问题2:但是在Player.cpp中进行的调用呢?它会使用TmPlayer.cpp中的定义,

这取决于。由于Player不能成为实例化对象,所以当对它进行调用时(好像有一个Players列表,就像上面的例子一样),它必须找出对象真的是什么 - 这将是一些子类播放器。如果子类碰巧是TmPlayer,那么它将调用TmPlayer类的Tm_doPost()实现:

void TmPlayer::Tm_doPost()

但是,如果碰巧是Player的另一个(理论上的)子类,例如&#34; TmPlayer2&#34;,那么它将调用实现的Tm_doPost():

void TmPlayer2::Tm_doPost()

你知道,调用的方法实际上取决于实际的实例类。

  

你的问题3:或者只会像Player.h中那样返回0?

在Player.h中,我认为你指的是:

virtual void Tm_doPost() = 0;

并不意味着它会返回任何东西。只是一些语法告诉编译器这个函数不会在这个类中实现 - 只允许子类实现它。

语法可能是

virtual void Tm_doPost() ~ $;

并完成工作 - 这对它没有特别的意义。我想一个好看的方法就是说这个函数在这个类中的实现等于0(或者没有)。

  

你的问题4:在Player.h和Team.h中,关键字virtual有什么作用?

无论何时调用该函数,它都会查找实例对象的实际功能,并执行该功能。 它也使得任何子类都可以选择通过声明具有相同名称的另一个函数来覆盖该函数。但是,他们也可以选择不覆盖它,然后可以使用父类的默认函数。 (如果没有&#34; = 0;&#34;语法 - 如果它不是纯虚拟功能。 如果它是一个纯虚函数并且你没有在子类中覆盖它,那么该子类也不能被实例化,并且它的子类只能实例化如果它它会覆盖它

最终评论&amp;建议:

这是我对虚拟功能的最好理解,但我强烈推荐本网站的第12章:http://www.learncpp.com/cpp-tutorial/122-virtual-functions/这是我学到的知识。 我老实地发现,学习虚函数的最好方法是遵循在线教程,同时做一些练习程序实验,制作一堆类,一些继承自其他类,并具有不同的功能,然后我找到了根据打印到终端的内容,执行哪个功能。

当然,沿途使用堆栈溢出有助于解决与bug相关的问题。

答案 1 :(得分:0)

声明为

的函数
virtual void Tm_doPost() = 0;

不返回0.它们是纯虚拟,并定义该类的接口。您询问的两个子类都将调用其祖先方法,Player::doTurn()方法将调用当前对象的 Tm_doPost()方法。

使用对象的虚方法表调用使用virtual关键字声明的成员函数。因此,当实例化类的对象时,通过引用存储在表中的地址来调用所有虚拟方法。这就是为什么超类Player不需要知道Tm_doPost方法的实现。

答案 2 :(得分:0)

当你调用这样的函数时,

void Player::doTurn()
{
    //...
    Tm_doPost();
    //...
}

即。在类Player的成员函数中,编译器应该在该类中查找函数,在这种情况下恰好是没有定义的纯虚函数,因此您无法调用它。这个名称查找业务几乎与文件无关。

我不知道你的代码是做什么的,但是既然你有一个Team,一个Player和一个TeampPlayer类,我假设你正在尝试聚合一个团队中的玩家并通过调用来实现多态性tm_doPost()?这是做到这一点的方法

#include <iostream>
struct player{
    virtual void foo(void){std::cout<<"player::foo";}
};
struct teamPlayer: public player{
    virtual void foo(void){std::cout<<"teamPlayer::foo";}
};

然后当你与函数foo;

进行动态绑定时
int main(int argc, char** argv){
     teamPlayer t;
     player* p = &t;
     p->foo(); //outputs "teamPlayer::foo"
     return 0;
}