Problems implementing polymorphism

I wrote a program that uses virtual functions to achieve polymorphism. I have a main user class that blindly calls a method, which in its opinion is common objects (although they really need to be specialized). These objects belong to classes that override pure virtual functions in their base classes. The following adapted code should demonstrate my setup:

Generic class (BaseConfig) in BaseConfig.h:

class BaseConfig { public: ... virtual void display() const = 0; ... } 

A specialized version of the above general class (SpecialConfig) in SpecialConfig.h:

 class SpecialConfig : public BaseConfig { public: ... void display() const; ... } 

Implementation of the above specialized class in SpecialConfig.cpp:

 ... void SpecialConfig::display() const { // print some text } ... 

Now, when I create the BaseConfig pointer and set it to the address of the SpecialConfig object, calling display () calls the display () function of the SpecialConfig class, as expected. However, things disagree with what I expect in the following code snippets to such an extent that for some reason, after the SpecialConfig objects return to the BaseConfig queue, calling the display () function no longer hits the display function () in SpecialConfig but instead tries to use the display () function in BaseConfig, causing the program to exit.

Here is a general class for generating configuration permutations. We will call it BaseRuleSet in BaseRuleSet.h:

 class BaseRuleSet { public: ... virtual queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const = 0; ... } 

The getValidChildConfigurations function will be overridden in the specialized RuleSet classes, as shown in the SpecialRuleSet class from SpecialRuleSet.h:

 class SpecialRuleSet : public BaseRuleSet { public: ... queue<BaseConfig *> getValidChildConfigurations(BaseConfig * c) const; } 

Implementation of the above class in 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; } ... 

As shown in the comment above, polymorphism is still working correctly at this point, since the specialized display function is still falling. However, in this last piece of code (shown below) everything falls apart. This is the User class from User.cpp:

 ... 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 } 

As the last comment in the above example shows, the last call to display () actually tries to use a pure virtual function in BaseConfig instead of the embedded custom version in SpecialConfig.

My thoughts are either a limitation or another way of doing things in C ++ that I don’t know about, or there are errors in my implementation. Can someone help clarify this for me?

Thanks.

+4
source share
2 answers

The problem has nothing to do with polymorphism. Your actual problem is as follows.

 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. 

You see that in C ++ a local variable lives as long as its volume. The specialConfigA object above will be destroyed as soon as getValidChildConfigurations returns, after which the pointer you saved in the queue points to ... something undefined. Therefore, when you try to call a method through it, you get undefined behavior, which is a crash in your case.

The solution is to dynamically allocate the SpecialConfig object:

 BaseConfig * baseConfigA = new SpecialConfig; 

This means that the object will be destroyed only when delete called. This is both a good and a bad thing: it no longer goes beyond the scope, but you should not forget to use delete when you are done, otherwise the memory will leak. The solution would be to use a smart pointer to execute delete for you. For this purpose, C ++ 11 has the class std::shared_ptr . If you are still stuck in C ++ 03, you can use boost::shared_ptr .

If you cannot or do not want to use smart pointers, then keep in mind that the queue constructor does not call delete in its content, so you have to scroll it to one point and delete everything.

+7
source
 SpecialConfig specialConfigA; SpecialConfig specialConfigB; baseConfigA = &specialConfigA; baseConfigB = &specialConfigB; 

You specify objects that were once on the stack. That's why it crashes - there are no more objects.

+3
source

Source: https://habr.com/ru/post/1391310/


All Articles