多态实现问题

时间:2012-01-16 19:20:00

标签: c++ polymorphism

我编写了一个程序,它使用虚函数来实现多态。我有一个主要的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 ++中有限制或不同的做事方式,我不知道或者我的实现中存在错误。任何人都可以帮我澄清一下吗?

感谢。

2 个答案:

答案 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;

你指向曾经在堆栈上的对象。这就是崩溃的原因 - 物体不再存在。