3

基本クラスから派生したすべてのクラスに特定の API を適用したいと考えています。通常は、純粋に仮想関数を持つ抽象基本クラスを使用してこれを行います。しかし、派生型を返す関数をどのように処理しますか? そのタイプの機能を強制するにはどうすればよいですか?

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

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

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

この例では、「メンバー関数の抽象戻り値の型が無効です」のようなエラーが発生します。ポインターを返すことを提案するいくつかの回答を見てきましたが、そのために動的メモリに浸りたくはありません。割り当てられたすべてのポインターを追跡することは、特別な種類の地獄になります。何か案は?

4

3 に答える 3

2

仮想関数がクラスへのポインターまたは参照を返す場合、基本クラスから継承して関数をオーバーライドするクラスは、戻り値の型を、元の戻り値の型から派生したクラスへのポインターまたは参照に変更できます。

base は抽象的であるため、値で返すことはできないため、実際にそれ自体を作成することはできません。

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

仮想関数と基本クラスを使用する場合、通常は動的割り当てを使用してオブジェクトを作成する必要があります。メモリの管理に役立つスマート ポインターを調べることをお勧めします。

于 2013-08-01T20:19:03.783 に答える
1

あなたの例では、func「同じ関数」にはならないため、バリアントderiv1deriv2バリアントは異なる仮想関数を持ちません。

残念ながら、ポインターを返す以外に方法はありません。動的に割り当てられたメモリへのポインターである必要はありません (たとえば、thisまたはへのポインターを返すこともできますが、へのポインターstatic deriv2 anObject;である必要がありますbase。[または、参照しますが、同じ問題が適用されます]。

この主な理由 (「関数は戻り値の型だけで区別できない」という事実は別として) は、次のような一般的なコードがある場合です。

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

deriv1ここで、またはderiv2を のサイズに切り取った [スライス]か、 -size オブジェクトbaseよりも大きいオブジェクトをコピーしたかのどちらかです。どちらも何のメリットもありません。[私は、これに対する REAL ユースケースでは、実際にはオブジェクトの名前よりも多くの点で異なると想定しています。そうでなければ、まったく無意味です。そして、それはもちろんから継承しています]。basebasederiv1deriv2basederiv1deriv2base

つまり、未知の型のオブジェクトを でコピーすることはできません=。また、返される型を知る必要がある場合、仮想関数を使用してもまったく意味がありません。

于 2013-08-01T20:30:38.240 に答える
0

基本的には、「コード内で deriv1 を deriv2 に置き換えたい場合は、これらの関数を実装する必要があります」という言い方です。

上記の引用から、次のようなものが必要なようです。

#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() );
}

これがあなたの引用に当てはまることがわかります。D2 は、ドライバーの観点からシームレスに D1 を置き換えます...

于 2013-08-01T21:46:47.163 に答える