1

我很确定这是一个长期 c++ 用户的简单问题,这应该是一个模式,或者问题应该以任何其他方式解决,但鉴于我是 Python 开发人员和 C++ 的新手,我不知道它是怎么回事通常完成。

假设我有一个类,我想在其中存储一个指向对象的指针,该对象可以是尊重接口的两个不同类中的一个,例如:

class AllPlayers
{
  public:
  virtual void play();
};

class VlcPlayer: public AllPlayers
{
  public:
  virtual void play();
};

class Mplayer: public AllPlayers
{
  public:
  virtual void play();
};

class MyMediaPlayer
{
  public:
  MyMediaPLayer(int playerType);
  AllPlayers m_player;
};

MyMediaPlayer::MyMediaPlayer(int PlayerType)
{
  if (PlayerType == 0) {
    VlcPlayer tmp_player;
    m_player = static_cast<AllPlayers> (tmp_player);
  }
  else {
    Mplayer tmp_player;
    m_player = static_cast<AllPlayers> (tmp_player);
  }
}

MyMediaPlayer test(0);
test.play();

首先,我知道这行不通,这似乎很正常,但我怎么能得到这种效果呢?我想拥有一个类的成员,我将使用相同的方法,使用接口实现,并且我想避免每次我要使用其中一个时都尝试强制转换为每个派生类他的方法。

4

3 回答 3

2

C++ 是基于值的,也就是说,如果你创建了一个给定类型的对象,你就真的拥有了这个类型的对象。这不能很好地与动态多态性一起使用。要获得动态多态性,您可以使用指针或对实际对象的引用。为了获得直接的生命周期,您通常会在堆栈上分配相应的对象(如果您曾经使用指向基的指针释放派生类型的对象,请确保您的基类具有虚拟析构函数)。有了这个,你应该已经准备好了:只需通过指向基类的指针调用基类的虚函数:当你覆盖派生类中的函数时,这就是被调用的函数。

于 2012-09-17T22:06:42.857 回答
1

如果你写

AllPlayers m_player;

那将是一个实例,AllPlayers并且不能是从它派生的类的实例。

您应该改为使用指针并在堆栈上分配类。

例如:类 MyMediaPlayer { public: MyMediaPLayer(int playerType); 〜我的媒体播放器();所有玩家 m_player; };

MyMediaPlayer::MyMediaPlayer(int PlayerType)
{
  if (PlayerType == 0) {
    m_player = new VlcPlayer;
  }
  else {
    m_player = new Mplayer;
  }
}

MyMediaPlayer::~MyMediaPlayer()
{
  if (0 != m_player) {
    delete m_player;
    m_player = 0;
  }
}

正如@xception 所建议的那样,使用 ofunique_ptr可以使您不必编写代码来释放实例。

正如@DietmarKühl 正确指出的那样,您应该始终在根类(本身不派生自其他类的基类)中声明一个虚拟析构函数,就像AllPlayers.

class AllPlayers
{
  public:
  virtual ~AllPlayers();
  virtual void play(); // note: this should probably be pure virtual.
};
于 2012-09-17T22:17:22.580 回答
1

这不起作用的原因通俗地称为对象拼接。(或者,对于那些哈利波特读者来说,对象拼接)

让我们看一个例子:

class Foo
{
public:
    int bob;
    float fred;

    // Foo(const Foo& otherfoo); // implicit copy constructor
};

class Bar : public Foo
{
public:
    double gabe; // gabe newell is fat
    char steve;  // steve jobs is thin

    // Bar(const Bar& otherbar); // implicit copy constructor
};

int main()
{
    Foo f;
    Bar b;

    f.bob = 10;
    f.fred = 1.5;

    b.bob = 15;
    b.fred = 2.5;
    b.gabe = 1.77245385091; // sqrt(pi)
    b.steve = -4;

    f = Foo(b);
    return 0;
}

这是合法有效的。问题是,Foo 的隐式复制构造函数被调用,而 Foo 的复制构造函数对 Bar 是什么一无所知。只是它包含了 Foo 所拥有的一切,以及一些额外的无关紧要的废话。因此,只有 Foo 的数据被保留;酒吧独有的数据被拼接掉。

重要的是要注意这是定义的行为:它完全按照您的指示行事。基类的子类和基类之间的转换是隐式的。此外,复制构造函数的行为是隐式的。

同样重要的是要注意,在底层,C++ 指针和引用以相同的方式工作。通过引用将 Bar 传递给 Foo 的复制构造函数是非常明智的,这种通过引用传递不会产生对象的副本。这与使用指针相同。

实际的拼接是复制构造函数咬得比它可以咀嚼的更多的直接结果。它得到一个状态比预期更多的对象,它唯一的选择是忽略额外的状态。

使用 python,这不会发生,因为所有内容都隐式存储为引用类型。由于您只使用引用(对象本身被抽象出来),因此您永远不会有机会意外拼接对象。

于 2012-09-17T22:21:20.567 回答