【问题标题】:Polymorphism implementation problems多态实现问题
【发布时间】:2012-01-16 19:19:59
【问题描述】:

我写了一个程序,它使用虚函数来实现多态性。我有一个主 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() 函数。但是,事情与我在以下代码 sn-ps 中所期望的不同,由于某种原因,在 BaseConfig 队列中返回 SpecialConfig 对象后,在它们上调用 display() 函数不再命中 display() 函数在 SpecialConfig 中,而是尝试在 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;

}
...

如上面的评论所示,此时多态性仍然正常工作,因为专门的显示功能仍在被击中。但是,在最后一个代码 sn-p(如下所示)中,一切都崩溃了。这是来自 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++ 中存在我不知道的限制或不同的做事方式,或者我的实现中存在错误。谁能帮我澄清一下?

谢谢。

【问题讨论】:

  • getValidChildConfigurations 返回指向函数完成后不存在的对象的指针。

标签: c++ polymorphism


【解决方案1】:

这个问题与多态性无关。您的实际问题如下。

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 一切。

【讨论】:

    【解决方案2】:
    SpecialConfig specialConfigA;
    SpecialConfig specialConfigB;
    
    baseConfigA = &specialConfigA;
    baseConfigB = &specialConfigB;
    

    您指向的是曾经在堆栈中的对象。这就是它崩溃的原因 - 对象不再存在。

    【讨论】:

      猜你喜欢
      • 2020-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      • 2011-07-22
      相关资源
      最近更新 更多