开发者

Return-type polymorphism for pass-by-value

开发者 https://www.devze.com 2023-01-04 08:20 出处:网络
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.

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,
};

Howev开发者_StackOverflow社区er, 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?


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.


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


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!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号