Return-type polymorphism for pass-by-value

岁酱吖の 提交于 2019-12-21 07:38:07

问题


I'm not sure if the question title is accurate... Let me start by explaining my original simple scenario, and then move on to explain what would I like to do, but can't.

Originally, I had something like:

class Operand;

Operand genOperandA() { ...; return Operand(); }
Operand genOperandB() { ...; return Operand(); }
... // more operand-generation functions

typedef Operand (*OpGen)();

// Table of function pointers
static const OpGen generators[] =
{
    genOperandA,
    genOperandB,
    ...
};

// Function to do some operation on the operand
void operate(Operand& op);

...

// Example call
operate(generators[1]());

So far so good (I think). However, there are now several derived operand types, e.g. class RegisterOperand : public Operand. I have new, dedicated genOperand functions that ideally would return instances of the derived types. But I can't do this:

Operand genOperandC() { ...; return RegisterOperand(); }

and I can't do this:

RegisterOperand genOperandC() { ...; return RegisterOperand(); }

static const OpGen generators[] = 
{
    ...
    genOperandC,
};

However, I know this would work if I were to return reference or pointer types, so the only option I currently have is something like:

Operand *genOperandC() { ...; return new RegisterOperand(); }

which now requires explicit cleanup which wasn't necessary originally.

Any alternatives I haven't considered?


回答1:


There might be other designs that doesn't require you to use pointers, but if you need or want to go this way, this might interest you.


If returning a pointer is a problem (because of the need to "clean-up" things), you definitely should consider using smart pointers as return type.

Here is an example of your factory method with smart pointers:

boost::shared_ptr<Operand> genOperandC()
{
  return boost::shared_ptr<Operand>(new RegisterOperand());
}

This way, you won't have to call delete manually: it will be done by the destructor of boost::shared_ptr<Operand> for you when required.

If afterwards you need to cast the resulting pointer, boost provides casting functions as well:

boost::shared_ptr<Operand> op = genOperandC();

boost::shared_ptr<RegisterOperand> rop =
  boost::dynamic_pointer_cast<RegisterOperand>(op);



回答2:


You can wrap:

class Operand
{
public:

private:
  std::unique_ptr<OperandImpl> mImpl;
};

This is similar to a Strategy Pattern: the actual operand behavior is hidden, and accessible through a Non-Virtual Interface. The user get a copy of Operand, she does not need to know anything about its internal and can use it, and you are free to implement various derived behaviors.




回答3:


I know this question was asked some time ago but I recently bumped into this problem myself and I came up with a different solution that I though could be helpful here.

So the idea is to make a wrapper to manage the pointer but to also support copying the wrapper unlike the unique pointer which can only be moved.

class PolymorphicType {
public: 
     /* 
         Class interface ...
     */
     PolymorphicType() { it = nullptr; }
     virtual ~PolymorphicType() { if(it != nullptr) delete it; }         

     PolymorphicType& operator=(const PolymorphicType& org) {
          //Clone the derived type and store it
          if(org.it != nullptr)
              it = org.it->clone();
     }
private:
     Base* it;
};

Each derived class must now implement it's own clone method and you are good to go! And just in case here is a nice post explaining how cloning of derived types works: Copying derived entities using only base class pointers, (without exhaustive testing!) - C++

Hope this helps someone out!



来源:https://stackoverflow.com/questions/3082995/return-type-polymorphism-for-pass-by-value

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!