Abstract base class for derived classes with funct

2019-07-24 17:14发布

I would like to force a certain API for all classes derived from the base class. Normally, you do that using an abstract base class that has purely virtual functions. However, how do you handle functions that return the derived type? How do I go about forcing that type of function?

struct base
{
    virtual base func() = 0;
};

struct deriv1 : base
{
    deriv1 func();
};

struct deriv2 : base
{
    deriv2 func();
};

This example will give an error like "invalid abstract return type for member function". I've seen some answers that suggest returning pointers, but I don't particularly want to dip into dynamic memory for that and keeping track of all the allocated pointers would be a special kind of hell. Any ideas?

3条回答
别忘想泡老子
2楼-- · 2019-07-24 17:52

In your example, the func won't be "the same function", so the deriv1 and deriv2 variants won't have a different virtual function.

Unfortunately, there is no other alternative than to return a pointer - it doesn't have to be a pointer to dynamically allocated memory (you could for example return a pointer to this or a static deriv2 anObject; - but it needs to be a pointer to base. [Or a reference, but the same problem applies].

The main reason for this (aside from the fact that "functions can't be differentiated only on return type") is that if you have some generic code that looks something like this:

vector<base*> v;
... stuff a bunch of `dervi1` or `deriv2` objects into v. 
for(i : v)
{
    base b = i->func();
}

Now, either you have now cut off [sliced] your deriv1 or deriv2 into the size of a base, or you'd have copied an object that is larger than base into a base-size object - neither of which will be of any benefit whatsoever. [I'm assuming that in the REAL use-case for this, deriv1 and deriv2 are in fact different from base by more aspects than the name of the object - otherwise, it's quite pointless. And that deriv1 and deriv2 are inheriting from base, of course].

In other words, you can't copy an object of unknown type with =. And it's absolutely no point in having a virtual function if you have to know what type it returns.

查看更多
家丑人穷心不美
3楼-- · 2019-07-24 18:05

When a virtual function returns a pointer or reference to a class, a class which inherits from the base class and overrides the function is allowed to change the return type to a pointer or reference to a class which is derived from the original return type.

You can't return base by value as it is abstract so you can't actually create one by itself.

http://en.wikipedia.org/wiki/Covariant_return_type

When using virtual functions and base classes, you usually have to use dynamic allocation to create your objects. I suggest you look into smart pointers to help manage the memory.

查看更多
SAY GOODBYE
4楼-- · 2019-07-24 18:06

Blockquote

Basically a way of saying "If you want to replace deriv1 with deriv2 in your code, you need to implement these functions"

Blockquote

From your quote here above, it looks like you want something like this:

#include <memory> //for unique_ptr
#include <iostream>

struct Base
{
  virtual void doX() = 0;
  virtual void doY() = 0;
  virtual ~Base(){}
};

struct D1 : Base
{
  virtual void doX()
  {
    std::cout << "D1::doX()" << std::endl;  
  }
  virtual void doY()
  {
    std::cout << "D1::doY()" << std::endl;  
  }
};

struct D2 : D1
{
  virtual void doX()
  {
    std::cout << "D2::doX()" << std::endl;  
  }
  virtual void doY()
  {
    std::cout << "D2::doY()" << std::endl;  
  }
};

//From this point on you can do various things:

void driver()
{
  Base* base = new D1;//
  base->doX(); //Calls D1::doX()
  D1* d1 = new D2;
  d1->doX(); //Calls D2::doX()
}
// or...
void driver( Base* base )
{
  //A way to replace Base with D1 with D2 depending
  // on how driver was called.
}

//Finally, maybe you want a factory to create the correct
// derived type based on which factory was instantiated.

// Creates family of products, each item representing the base
// in it's hierarchy - only one shown here...
struct AbstractFactory
{
  virtual std::unique_ptr<Base> create() const = 0;
  protected:
    virtual ~AbstractFactory(){}
};

struct D1Factory : AbstractFactory
{
  //Doesn't matter if <Derived> is returned, because it adheres
  // to interface of Base (isA base), and correct functions are
  // found polymorphically 
  virtual std::unique_ptr<Base> create() const
  {
    return std::unique_ptr<Base>( new D1 ); 
  }
};
struct D2Factory : AbstractFactory
{
  //Doesn't matter if <Derived> is returned, because it adheres
  // to interface of Base (isA base), and correct functions are
  // found polymorphically 
  virtual std::unique_ptr<Base> create() const
  {
    return std::unique_ptr<Base>( new D2 ); 
  }
};

void driver( const AbstractFactory& factory )
{
  std::unique_ptr<Base> base( factory.create() );
  base->doX();
  base->doY();
  //Memory deallocated automagically...
}

int main()
{
  driver( D1Factory() );
  driver( D2Factory() );
}

You'll see that this holds true to your quote. D2 replaces D1 seamlessly from the perspective of driver...

查看更多
登录 后发表回答