我在C ++中遇到一些不寻常的行为,我想了解更多。我有一个具有纯虚函数Instrument
的类play
。类Flute
和Trumpet
继承自Instrument
并实施play
。 Trumpet
还有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 ++的放纵。
答案 0 :(得分:4)
这里发生了什么?这是不确定的 行为?
正在发生的事情是transmogrify()正在删除传入函数的对象,然后创建一个新的Flute对象,将其分配给本地参数变量(instrument),然后在函数返回时永久丢失。因此,如果调用代码稍后尝试取消引用它们传入的(现在悬空)指针,那么你的内存泄漏,以及调用代码中可能存在未定义的行为。
要获得您正在寻找的行为,您应该通过引用传递指针:
void transmogrify(Instrument * & instrument)
...这样,调用者的指针将被修改为在函数返回后指向新的Flute对象。
答案 1 :(得分:2)
您没有提供足够的代码来完全理解该问题。但是你发布的方法非常有问题,所以我会解决这个问题。
它将指针作为参数。然后代码删除指针指向的内容并为其指定一个新值。
问题是此方法接收指针的副本。因此调用者仍然具有旧指针,现在指向已删除的对象。如果调用者使用该指针,它将访问应用程序不再拥有的内存。
答案 2 :(得分:1)
transmogrify
不会通过引用(或者甚至指向指针)获取其参数,因此虽然它可以释放传入的值,但它不能返回新值。调用者仍然有旧指针,现在指向已删除的内存。使用它是未定义的行为(这意味着它似乎仍然可以工作)。
答案 3 :(得分:-1)
所以,这个函数的“契约”表示它将需要一个Instrument
,并且被允许做它想做的任何事情(因为const
没有在函数签名中的任何地方使用)。因此,您有一个指向特定内容(Trumpet)的指针,并将其传递给一个不保证不会弄乱它的函数。您有指针的对象被删除并替换为。只要那个类型为Instrument
的东西,那么该函数就没有破坏它的契约。
出于好奇,你会尝试用这种模式做什么?