7

2 つのインターフェイスを実装し、1 つのクラスを継承するクラスがあります。したがって、一般的には次のようになります。

class T : public A, public IB, public IC {
};

コードには がありますが、IB *実際には を使用できるポイントが 1 つありA *ます。私は動的キャストがこれを好むことを望んでいました:

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<A *>(b_ptr);

残念ながら、これは機能しません。これを行う適切な方法はありますか?または、回避策を実装する必要がありますか? IBと の両方をIC仮想的に継承することを考えましAたが、前回 IIRC を試してみたところ、いくつかの複雑さが望ましくないことがありました。

何かご意見は?

編集:そうそう、これはプラグイン API の一部であるため、残念ながら、T必要なタイプに直接アクセスすることはできませんA *。私の例は隣り合っていますが、前述のように、より複雑です。基本的に、2 つの共有ライブラリがあります。TT1(私が持っている場所IB *) は両方とも、プラグイン API を実装し、共有ライブラリの内部にあるクラスです。

明確にするために:これは私の典型的なプラグインのより具体的な例です(それらは別々のライブラリにあります):

プラグイン A:

class PluginA : public QObject, public PluginInterface, public OtherInterface {
};

プラグイン B:

class PluginB : public QObject, public PluginInterface {
    // in here, I have a PluginInterface *, but really could use a QObject *
    // unfortunately, PluginB has absolutely no knowledge of the "PluginA" type
    // it just so happens that my PluginInterface * pointer points to an object of type
    // PluginA.
};

編集: 問題は、pluginA と pluginB が異なる共有ライブラリにあることだと思います。おそらく、rtti はモジュールの境界を越えていません。私のテストでは人々の例がうまく機能しているように見えるので、これが当てはまるかもしれないと思います。具体的には、「nm」を実行すると、pluginB には「PluginA の typeinfo」がありません。これが問題の核心かもしれません。この場合は、仮想継承またはcast_to_qobject()インターフェイスの 1 つで仮想関数を使用して回避する必要があります。

4

5 に答える 5

7

各クラスには少なくとも 1 つの仮想メソッドがありますか? そうでない場合は、問題があります。各クラスに仮想デストラクタを追加すると、問題を解決できます。

以下は私にとってうまくいきました:

class IC
{
public:
    virtual ~IC() {}
};

class IB
{
public:
    virtual ~IB() {}
};

class A
{
public:
    virtual ~A() {}
    void foo() { /* stick a breakpoint here to confirm that this is called */ }
};

class T : public A, public IB, public IC 
{
public:
    virtual ~T() {}
};


int main(void)
{
    IB *b_ptr = new T;
    A *a_ptr = dynamic_cast<A *>(b_ptr);
    a_ptr->foo();
    return 0;
}

編集:

すべての新しい情報と異常な動作 (コードは正常に動作するはずです!) の後、次のことは役に立ちますか? IObject というインターフェイスを導入し、仮想継承を使用して、この基本クラスのコピーが 1 つだけ存在するようにします。IObject にキャストしてから A にキャストできますか?

class IObject
{
public:
    virtual ~IObject() {}
};

class IC : virtual public IObject
{
public:
    virtual ~IC() {}
};

class IB : virtual public IObject
{
public:
    virtual ~IB() {}
};

class A : virtual public IObject
{
public:
    virtual ~A() {}
    void foo() { /* stick a breakpoint here to confirm that this is called */ }
};

class T : virtual public A, virtual public IB, virtual public IC
{
public:
    virtual ~T() {}
};


int main()
{
    IB *b_ptr = new T;
    A *a_ptr = dynamic_cast<A *>( dynamic_cast<IObject *>(b_ptr) );
    a_ptr->foo();
    return 0;
}

それが正しい解決策であることを示唆しているわけではありませんが、何が起こっているのかについての情報を提供するかもしれません...

于 2009-01-14T05:37:54.100 に答える
5

これを行う適切な方法はありますか?または、回避策を実装する必要がありますか? IB と IC の両方が事実上 A から継承されるようにすることを考えましたが、前回 IIRC を試したとき、いくつかの複雑さが望ましくないものになっていることがわかりました。

IB と IC の定義はあなたの管理下にあると思います。

COM インターフェイスが Windows で動作する方法があります。これらはあなたがやりたいことをします、すなわち:

  • あるインターフェースから別のインターフェースへのキャスト
  • 実装は呼び出し元には不透明です
  • 実装のみが実装するインターフェースを知っています

これを行うと、次のようなことができます(未テストのコード先)...

interface IQueryInterface
{
  IQueryInterface* queryInterface(const Guid* interfaceId);
};

interface IB : public abstract IQueryInterface
{
  ...
};

interface IC : public abstract IQueryInterface
{
  ...
};

//within your implementation class
IQueryInterface* T::queryInterface(const Guid* interfaceId)
{
  if (matches(interfaceId,GUID_IB))
    return (IB*)this;
  if (matches(interfaceId,GUID_IC))
    return (IC*)this;
  if (matches(interfaceId,GUID_A))
    return (A*)this;
  return 0;
}

これをより単純に、よりハードコード化したバージョンは次のようになります。

class A; //forward reference
interface IB
{
  virtual A* castToA() { return 0; }
};
class T : public A, IB, IC
{
  virtual A* castToA() { return this; }
};
于 2009-01-14T05:41:45.573 に答える
4

私は最終的にそれを理解しました.ダニエル・ポールは「横向きdybnamic_cast」が許されるべきであるという点で正しかった. 私の問題は、コードに共有ライブラリが含まれているためです。PluginA の typeinfo は PluginB では利用できませんでした。私の解決策は、ロードプロセスに効果RTLD_NOW的に追加することでしたRTLD_GLOBAL

技術的にはそうだった

loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);

私はQtのプラグインシステムを使用していますが、同じ違いがあるためです。これらのフラグは、読み込まれたライブラリからのすべてのシンボルを強制的に解決し、他のライブラリから見えるようにします。したがって、それを必要とするすべての人が typeinfo を利用できるようにします。これらのdynamic_castフラグが配置されると、期待どおりに機能しました。

于 2009-01-24T02:51:23.950 に答える
3

最初に T* にキャストし、次に A にキャストします。

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<T *>(b_ptr);

一般的に IB が A にキャスト可能である場合、IB は A から継承する必要があります。

編集:私はこれを試してみましたが、動作します.Eはメインメソッドのコンパイル時には不明であることに注意してください.

struct A
{
    virtual ~A() {}
};

struct C
{
    virtual ~C() {}
};

A* GetA();

int main()
{
    C *y = dynamic_cast<C *>(GetA());
    if (y == NULL)
        cout << "Fail!";
    else
        cout << "Ok!";
}

struct E : public A, public C
{
}; 

A* GetA() { return new E(); }
于 2009-01-14T05:16:53.190 に答える
1

私も最近同じような症状で悩んでいます。詳細については、GCC の FAQ エントリを参照してください。

http://gcc.gnu.org/faq.html#dso

RTLD_* フラグを使用して dlopen を指示する以外に、この問題のいくつかの具現化はリンカーによっても解決できます。-E および -Bsymbolic オプションを参照してください。

于 2009-07-20T13:54:44.183 に答える