多态实现问题

S.C*_*.C. 4 c++ polymorphism

我编写了一个程序,它使用虚函数来实现多态.我有一个主要的User类,盲目地调用它认为是通用对象的方法(虽然它们实际上应该是专门的).这些对象来自覆盖其基类中的纯虚函数的类.以下改编的代码应该演示我的设置:

BaseConfig.h中的泛型类(BaseConfig):

class BaseConfig {
public:
    ...
    virtual void display() const = 0;
    ...
}
Run Code Online (Sandbox Code Playgroud)

SpecialConfig.h中上述泛型类(SpecialConfig)的专用版本:

class SpecialConfig : public BaseConfig {
public:
    ...
    void display() const;
    ...
}
Run Code Online (Sandbox Code Playgroud)

在SpecialConfig.cpp中实现上述专用类:

... 
void SpecialConfig::display() const {
   // print some text
}
...
Run Code Online (Sandbox Code Playgroud)

现在,当我创建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;
    ...
}
Run Code Online (Sandbox Code Playgroud)

它的getValidChildConfigurations函数将在专门的RuleSet类中重写,如SpecialRuleSet.h中的SpecialRuleSet类所示:

class SpecialRuleSet : public BaseRuleSet {
public:
    ...
    queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const;
}
Run Code Online (Sandbox Code Playgroud)

在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;

}
...
Run Code Online (Sandbox Code Playgroud)

如上面的注释所示,此时多态性仍然正常工作,因为专用的显示功能仍然受到影响.但是,在最后一段代码片段(如下所示)中,一切都崩溃了.这是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


}
Run Code Online (Sandbox Code Playgroud)

正如上面示例中显示的最后一条注释,最后一次调用display()实际上尝试使用BaseConfig中的纯虚函数,而不是SpecialConfig中实现的专用版本.

我的想法要么是在C++中有限制或不同的做事方式我不知道或者我的实现中存在错误.任何人都可以帮我澄清一下吗?

谢谢.

Eti*_*tel 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.
Run Code Online (Sandbox Code Playgroud)

您可以看到,在C++中,局部变量与其范围一样长.specialConfigA上面的对象将在getValidChildConfigurations返回后立即销毁,之后存储在队列中的指针指向...未定义的内容.因此,当您尝试通过它调用方法时,会得到未定义的行为,这在您的情况下会崩溃.

解决方案是动态分配SpecialConfig对象:

BaseConfig * baseConfigA = new SpecialConfig;
Run Code Online (Sandbox Code Playgroud)

这意味着只有在调用时才会销毁对象delete.这既是好事也是坏事:它将不再超出范围,但你必须忘记delete在完成后使用,否则内存将泄漏.一个解决方案是使用智能指针delete为您完成.C++ 11具有std::shared_ptr用于此目的的类.如果你仍然坚持C++ 03,你可以使用boost::shared_ptr.

如果你不能或不想使用智能指针,那么请记住,queue构造函数不会调用delete其内容,因此你必须在某一点和delete 所有内容中循环它.