0

私は 3D エンジンを開発しています。次のインターフェイス クラスがあるとします。

class IA {
public:
    virtual ~IA() {}
    virtual void doSomething() =0;   
};

class IB {
public:
    virtual ~IB() {}
    virtual void bindA( IA* ) =0;
};

タイプ「IA」または「IB」のオブジェクトを取得したい場合は、使用されているバックエンド API (OpenGL など) に依存するファクトリから取得する必要があります。関数 IB::bindA(IA*) は IA の実装からデータにアクセスする必要があり、それを実現するにはstatic_cast、実装クラスに対して a を実行し、その要素に直接アクセスします。

のこの特定の使用法についてどう思うか知りたいのですがstatic_cast、設計が悪いと思いますか? それとも大丈夫だと思いますか?

エンジンはどのバックエンド API を使用しても同じインターフェイスを提供する必要があるため、IA から IB が必要とするものを事前に知ることができないため、仮想関数を使用してこれを実現できるとは思いません。

ありがとう

編集

エンジンには次の 2 つのクラスがあります。

class IHardwareBuffer {
public:
    virtual ~IHardwareBuffer() {}
    virtual void allocate( .... ) =0;
    virtual void upload( .... ) =0;
};

class IMesh {
public:
    virtual ~IMesh() {}
    virtual bindBuffer( IHardwareBuffer* ) =0;
    ...
};

IMesh と IHardwareBuffer クラスを一緒に「マージ」することはできますが、HardwareBuffer は頂点データを含むメモリの「愚かな」部分であり、メッシュは他のデータを含む 1 つまたは 2 つの HardwareBuffers であるため、あまり意味がありません。それら、頂点フォーマット、マテリアルなど。それらを別々のクラスにすることで、クライアント コードで複数のメッシュが共通の HardwareBuffer などを共有できるようになります。

4

3 に答える 3

1

設計の観点からは、実際にはかなり悪い考えのように思えます。

インターフェイスを使用する場合 (または、C++ にはそのような言語構造がないため、インターフェイスをシミュレートする場合)、それらを使用して、他の場所で必要なこれらのデータを公開します。したがって、実装しているオブジェクトがそのデータを取得するために何かにIBキャストする必要がある場合、それは明らかに十分なデータを公開していないか、実装しているオブジェクトが別のより広いインターフェイスも実装する必要があることを示しています。IAIAIA

ここでのコンテキストがわからないため、どちらのオプションが優れているか (または別のオプションがある場合) を判断するのは困難です。一般に、実際に必要でない場合はキャストを避ける必要があり、ここでもちろん必要ありません。


編集

The engine has to provide the same interface no matter what backend API is being used, so I don't think I could achieve this using virtual functions because I can't know beforehand what is needed by IB from IA.- これは悪い設計です。

エンジンは、それを使用する実装とは完全に独立しているように、またはその逆の方法で作成する必要があります。これが、インターフェイス、基本クラス、ポリモーフィズムを使用するポイントです。別のエンジンを作成し、既存のエンジンと交換して、実装を変更することなくすべてが機能する必要があります。


編集(コメントに応じて):

より明確な解決策は、特定の実装ではなく、別のインターフェイスにキャストすることだと思います。

class A : public IA, public IInternalA
{
     // Implementation
};

// Inside B:
void B::Process(IA * a)
{
    IInternalA ia = dynamic_cast<IInternalA *>(a);

    if (ia != nullptr)
        // Do something
}

このようにして、実装から切り離すことはできますが (たとえば、2 つの独立した部分に分割することができます)、エンジン内ではすべてのクラスが互いに十分に認識して適切に動作します。

于 2013-06-10T05:06:56.187 に答える
0

オブジェクトには、実行時に vtable から読み取ることができる動的型と、ソース コードで宣言する静的型があります。

動的型に基づいて安全にキャストするには、 を使用しますdynamic_cast。vtable を見ずに動的な型を既に知っている場合は、 を に最適化できdynamic_castますstatic_cast。これにより、パフォーマンスが大幅に向上する可能性があり、有効である限り、そうしても問題はありません。

ただし、派生クラスの参照に頻繁にキャストするコードでは、関心の分離に問題が生じる可能性があります。クラス階層のポイントは、一般化することです。

dynamic_castキャストが無効な場合、参照形式は例外をスローするため、ポインターではなく参照を使用することをお勧めします。次に、次のようなことができます。

// Check dynamic type (and throw exception) for debug build only
#ifndef NDEBUG
#define downcast static_cast
#else
#define downcast dynamic_cast
#endif

Iopengl &glenv = downcast< Iopengl & >( myIA );

vtable (たとえば、グローバル フラグ) にアクセスせずに実際の動的な型を常に知っている場合opengl、vtable はもちろん冗長です。仮想ディスパッチを置き換えるフラグとブランチを使用して、プログラム全体を作成できます。

エンジンはどのバックエンド API を使用しても同じインターフェイスを提供する必要があるため、IA から IB が必要とするものを事前に知ることができないため、仮想関数を使用してこれを実現できるとは思いません。

既に述べたように、抽象基底クラスはインターフェイスを提供し、派生クラスはバックエンドを呼び出します。あなたの例は少し大雑把ですが、IA と IB はインターフェイスのように見えます。実装に関係なく、目標を達成するにはバックエンドに依存しない方法を定義する必要があります。

于 2013-06-10T05:21:47.310 に答える
0

さて、私はあなたの問題を理解していると思います。実質的に共通点がないバックエンドがいくつかあり、それらの違いを隠すエンジンでそれらを使用する必要があります。

問題は、2 つの型に共通点がない場合、共通の base から継承してはならないということです。そしてもちろん、ポインターを に保管するようなハックはvoid*、じゅうたんの下のほこりを一掃するだけです。だから、そうしないようにしましょう。

そのため、バックエンドごとにラッパーを提供する必要があります。すべてのラッパーは同じインターフェースに準拠する必要がありますが、実装に関する限り共通点はありません。ファクトリはラッパーを返す必要があります。

class IBackendWrapper
{
  public:
   ... backend pure virtual functions ...
};

class OpenGLBackendWrapper : public IBackendWrapper
{
  public:
   ... backend virtual function immplementations in terms of OpenGL ...
  private:
    ... OpenGL data ...
};

class X11BackendWrapper : public IBackendWrapper
{
  public:
   ... backend virtual function immplementations in terms of X11...
  private:
    ... X11 data ...
};

class BackendFactory
{
  public:
    IBackendWrapper* getbackend();
};

IBackendWrapperこれで、エンジンは具体的なバックエンドについてあまり気にせずに使用できます。

3D 抽象化が浅い場合、各ラッパーがエンジン全体になる可能性があります次に、エンジン クラスは単純なフォワーダーに退化します。これで結構です。

于 2013-06-10T05:56:59.417 に答える