我编写了一个程序,它使用虚函数来实现多态。我有一个主要的User类,盲目地调用它认为是通用对象的方法(虽然它们实际上应该是专门的)。这些对象来自覆盖其基类中的纯虚函数的类。以下改编的代码应该演示我的设置:
BaseConfig.h中的泛型类(BaseConfig):
class BaseConfig {
public:
...
virtual void display() const = 0;
...
}
SpecialConfig.h中上述泛型类(SpecialConfig)的专用版本:
class SpecialConfig : public BaseConfig {
public:
...
void display() const;
...
}
在SpecialConfig.cpp中实现上述专门类:
...
void SpecialConfig::display() const {
// print some text
}
...
现在,当我创建BaseConfig指针并将其设置为SpecialConfig对象的地址时,调用display()会按原样点击SpecialConfig类的display()函数。但是,事情与我在下面的代码片段中所期望的不同,在某种情况下,在BaseConfig队列中返回SpecialConfig对象之后,调用它们上的display()函数不再触及SpecialConfig中的display()函数。但是尝试在BaseConfig中使用display()函数,导致程序退出。
这是用于生成配置排列的通用类。我们将其称为BaseRuleSet.h中的BaseRuleSet:
class BaseRuleSet {
public:
...
virtual queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const = 0;
...
}
它的getValidChildConfigurations函数将在专门的RuleSet类中重写,如SpecialRuleSet.h中的SpecialRuleSet类所示:
class SpecialRuleSet : public BaseRuleSet {
public:
...
queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const;
}
在SpecialRuleSet.cpp中实现上述类:
...
queue<BaseConfig *> SpecialRuleSet::getValidChildConfigurations(BaseConfig * c) const {
queue<BaseConfig *> validChildConfigurations;
BaseConfig * baseConfigA;
BaseConfig * baseConfigB;
SpecialConfig specialConfigA;
SpecialConfig specialConfigB;
baseConfigA = &specialConfigA;
baseConfigB = &specialConfigB;
validChildConfigurations.push(baseConfigA);
validChildConfigurations.push(baseConfigB);
// validChildConfigurations.front()->display() works correctly here
return validChildConfigurations;
}
...
如上面的评论所示,此时多态性仍然正常工作,因为专用的显示功能仍然受到影响。但是,在最后一段代码片段(如下所示)中,一切都崩溃了。这是User.cpp中的User类:
...
void User::doStuff() {
BaseRuleSet * baseRuleSet;
SpecialRuleSet specialRuleSet;
baseRuleSet = &specialRuleSet;
BaseConfig * currentConfig;
/*
SpecialConfig specialConfig;
currentConfig = &specialConfig;
currentConfig->display(); // this works
*/
queue<BaseConfig *> childConfigurations = ruleSet->getValidChildConfigurations(currentConfig);
childConfigurations.front()->display(); // this does not work
}
正如上面示例中显示的最后一条注释,最后一次调用display()实际上尝试使用BaseConfig中的纯虚函数,而不是SpecialConfig中实现的专用版本。
我的想法是要么在C ++中有限制或不同的做事方式,我不知道或者我的实现中存在错误。任何人都可以帮我澄清一下吗?
感谢。
答案 0 :(得分:7)
问题与多态性无关。您的实际问题如下。
BaseConfig * baseConfigA; // Okay.
SpecialConfig specialConfigA; // Fair enough
baseConfigA = &specialConfigA; // Hmm, getting the address of a local variable?
validChildConfigurations.push(baseConfigA); // This should be okay as long as
// you don't return that queue...
return validChildConfigurations; // Oh dear.
你会发现,在C ++中,局部变量与其范围一样长。一旦specialConfigA
返回,上面的getValidChildConfigurations
对象将被销毁,之后存储在队列中的指针指向......未定义的内容。因此,当您尝试通过它调用方法时,会得到未定义的行为,这在您的情况下会崩溃。
解决方案是动态分配SpecialConfig
对象:
BaseConfig * baseConfigA = new SpecialConfig;
这意味着只有在您调用delete
时才会销毁该对象。这既是好事也是坏事:它将不再超出范围,但是当你完成时你不能忘记使用delete
,否则内存会泄漏。解决方案是使用智能指针为您执行delete
。为此,C ++ 11具有std::shared_ptr
类。如果您仍然使用C ++ 03,则可以使用boost::shared_ptr
。
如果您不能或不想使用智能指针,请记住queue
的构造函数不会在其内容上调用delete
,因此您必须循环通过它在某一点和delete
一切。
答案 1 :(得分:3)
SpecialConfig specialConfigA;
SpecialConfig specialConfigB;
baseConfigA = &specialConfigA;
baseConfigB = &specialConfigB;
你指向曾经在堆栈上的对象。这就是崩溃的原因 - 物体不再存在。