重新分配对象时奇怪的C ++行为

时间:2015-02-16 17:58:02

标签: c++

我在C ++中遇到一些不寻常的行为,我想了解更多。我有一个具有纯虚函数Instrument的类play。类FluteTrumpet继承自Instrument并实施playTrumpet还有toggleMute的其他方法Flute

很抱歉,之前只包含一个Gist的链接。这是完整的代码:

class Instrument
{
public:
  virtual void play() = 0;
};

class Flute : public Instrument
{
public:
  Flute() {};
  void play();
};

class Trumpet : public Instrument
{
protected:
  bool muted;
public:
  Trumpet() {muted = false;};
  void play();
  void toggleMute();
};


void Flute::play()
{
  std::cout << "Dude, play Jethro Tull!" << std::endl;
}

void Trumpet::play()
{
  if (muted) {
    std::cout << "Beep" << std::endl;
  } else {
    std::cout << "Honk" << std::endl;
  }
}

void Trumpet::toggleMute()
{
  muted = muted xor true;
  if (muted) {
    std::cout << "Mute on!" << std::endl;
  } else {
    std::cout << "Mute off!" << std::endl;
  }
}

void transmogrify(Instrument* instrument)
{
  if (instrument) {
    delete instrument;
    instrument = new Flute();
  }
}


int main(int argc, char **argv)
{
  Trumpet *horn = new Trumpet();

  horn->play();
  horn->toggleMute();
  horn->play();
  horn->toggleMute();
  horn->play();

  std::cout << std::endl;

  transmogrify(horn);

  horn->play();
  horn->toggleMute();
  horn->play();
  horn->toggleMute();
  horn->play();

  return 0;
}

在我正在学习的另一种语言中,这种行为是非法的。如果我有一个接受Instrument*的函数,我无法将它传递给一个被声明为Trumpet*的对象,因为语言设计者希望避免这种奇怪。不幸的是,这意味着某些我无法实现的设计会让我的生活变得更轻松;总而言之,即使一些奇怪的东西不时发生,我也更喜欢C ++的放纵。

4 个答案:

答案 0 :(得分:4)

  

这里发生了什么?这是不确定的   行为?

正在发生的事情是transmogrify()正在删除传入函数的对象,然后创建一个新的Flute对象,将其分配给本地参数变量(instrument),然后在函数返回时永久丢失。因此,如果调用代码稍后尝试取消引用它们传入的(现在悬空)指针,那么你的内存泄漏,以及调用代码中可能存在未定义的行为。

要获得您正在寻找的行为,您应该通过引用传递指针:

void transmogrify(Instrument * & instrument)

...这样,调用者的指针将被修改为在函数返回后指向新的Flute对象。

答案 1 :(得分:2)

您没有提供足够的代码来完全理解该问题。但是你发布的方法非常有问题,所以我会解决这个问题。

它将指针作为参数。然后代码删除指针指向的内容并为其指定一个新值。

问题是此方法接收指针的副本。因此调用者仍然具有旧指针,现在指向已删除的对象。如果调用者使用该指针,它将访问应用程序不再拥有的内存。

答案 2 :(得分:1)

transmogrify不会通过引用(或者甚至指向指针)获取其参数,因此虽然它可以释放传入的值,但它不能返回新值。调用者仍然有旧指针,现在指向已删除的内存。使用它是未定义的行为(这意味着它似乎仍然可以工作)。

答案 3 :(得分:-1)

所以,这个函数的“契约”表示它将需要一个Instrument,并且被允许做它想做的任何事情(因为const没有在函数签名中的任何地方使用)。因此,您有一个指向特定内容(Trumpet)的指针,并将其传递给一个不保证不会弄乱它的函数。您有指针的对象被删除并替换为。只要那个类型为Instrument的东西,那么该函数就没有破坏它的契约。

出于好奇,你会尝试用这种模式做什么?