0

多くの (数百の) ポインターを含む構造があります。各ポインターは異なる型ですが、それらはすべて共通の基本クラスから継承されます --- それを Base と呼びましょう。多重継承を使用しています。(これはすべて機械生成であるため、奇妙です。)

例えば:

class Data {
    Class1* p1;
    Class2* p2;
    Class3* p3;
    ...etc...
};

これらすべてに対して、Base で定義されたメソッドを呼び出したいと考えています。次のようなコードを生成できます。

void callFooOnData(Data* p)
{
    if (p1) p1->Foo();
    if (p2) p2->Foo();
    if (p3) p3->Foo();
    ...etc...
}

問題は、10 億もの異なる種類のデータがたくさんあり、上記のコードが非常に大きくなり、フットプリントに影響を与えていることです。だから私はこれをもっと賢いものに置き換えようとしています。

C では、単純に最初の構造体メンバーのアドレスとカウントを取得して、次のようにすることができます。

void callFooOnData(Data* p)
{
    callFooOnObjects(&p1, 3);
}

しかし、もちろん、これを C++ で行うことはできません。なぜなら、構造体のメンバーは統一された型ではなく、それらを Base* にキャストするには、それらを変更する必要があり、これにはポインターを変更する必要がある可能性があります。統一型のポインターは、メンバーごとに異なる方法で変更する必要がありますが、それはできません。

このようなことをC++で行う方法はありますか? これはすべて機械で生成されたコードなので、きれいである必要はありません。ありがたいことに --- そのサメはすでにジャンプされています --- しかし、可能な限り移植性が必要です...

(私は RTTI にアクセスできますが、パフォーマンス上の理由から、可能であれば避けたいと考えています。)

アップデート:

だから、私が知る限り... C++ではこれを行うことはできません。単純にできません。C では完全に単純であるという事実に惑わされました。C++ では、クラス階層内の 2 つの型の間でポインターを安全にキャストできるのは、最初から適切に型指定されたポインターがある場合のみです。もちろん、私はそうではありません。

したがって、これを回避するために根本的な問題を変更する必要があります: 私が思いついた解決策は、すべてのポインターを構造体に 2 回格納することです。 * メソッドの呼び出し用。もちろん、データ構造のサイズが 2 倍になるので、これは最悪です。

したがって、誰かがこれを確認したい場合 (私が間違っていることを証明したいと思います)、私は喜んで彼らの答えを正しいものとしてマークします...

4

6 に答える 6

4

各ポインターは異なる型ですが、それらはすべて共通の基本クラスから継承します --- それを Base と呼びましょう

何百ものメンバーを持つ代わりに、Baseクラス ポインターのコンテナーを保持するだけです。

class Data {
   std::vector<Base*> objects;
};

優れた設計では、各オブジェクトの型を実際に知る必要はありません。実際には、抽象化されている方がよいでしょう。具象クラスではなく、インターフェイスに対してプログラミングすることは常に良いことです。

それらを Base* にキャストすると、変更が必要になる場合があります

そうではありません。public継承の場合、キャストは暗黙的でなければなりません。

Foo()それらすべてを呼び出すだけで、基本クラスFoo()のメソッドにすることができvirtual、ポリモーフィズムを最大限に活用できます。

于 2012-07-08T18:26:50.137 に答える
0

ただし、もちろん、C ++ではこれを行うことはできません。これは、構造体のメンバーが均一な型ではなく、Base *にキャストすると変更が必要になる場合があり、ポインターの変更が必要になる場合があります。ユニフォームタイプでは、メンバーごとにポインターを異なる方法で変更する必要がありますが、それはできません。

悪い考えですが、これは真実ではありません。Cの場合と同じように、ポインター演算を使用してポインターを反復処理できます。ポインターはすべて同じサイズであり、共通の基本クラスがあります(構造アラインメントの問題などは考慮されていません)。その汚いですが、技術的には動作します。インスタンス自体をクラスのメンバーとして保持し、それらへのポインターではない場合は、これは異なります。

C ++ 11での最良の方法は、

std::vector< std::unique_ptr<Base> > objects;

http://www.drdobbs.com/cpp/c11-uniqueptr/240002708を参照してください

于 2012-07-08T19:10:55.860 に答える
0

コードも自動生成して、自動生成されたコードと「戦う」必要があります。ctags などのツールを使用して、自動生成されたクラスを「解析」し、ctags 出力からコードを自動生成できます。http://ctags.sourceforge.net/ctags.htmlを参照してください。

この重複の可能性がある質問: Iterate through struct variablesでアドバイスされているように、データをタプルにキャストすることもできます。

どちらが速いかはわかりません...

このソース コードを自動生成するツールを変更できる場合は、このツールを拡張するのが最善でしょう...

于 2012-07-08T19:38:05.177 に答える
0

別のオプション (コンパイル不可):

class DataManipulator 
{
public:
   DataManipulator(const Data& aData_in)
   {
      objects.add(aData_in->Class1);
        .
        .
   }
   void operate()
   {
       for(objects_iterator...)
       {
           if(*objects_iterator != NULL)
                 objects_iterator->Foo();
       }
   }
private:

   std::vector<Base*> objects;
};
于 2012-07-08T19:38:11.133 に答える
0

以前の回答を変更したくありませんでした。しかし、私はあなたのために働くべき別の解決策に行き着きます: ここに完全な例: http://ideone.com/u22FO

以下の主要部分:

struct C {
  A1* p1;
  A2* p2;
  A3* p3;
  A4* p4;
  // ...
};

template <class D, size_t  startNumber, size_t numMembers, bool notFinished>
struct FooCaller;
template <class D, size_t  startNumber>
struct FooSingleCall;

template <class D, size_t  startNumber, size_t numMembers>
struct FooCaller<D, startNumber, numMembers, false> {
   void operator() (D& d) {}
};

template <class D, size_t  startNumber, size_t numMembers>
struct FooCaller<D, startNumber, numMembers, true> {
   void operator() (D& d) {
       FooSingleCall<D,startNumber>()(d);
       FooCaller<D, startNumber + 1, numMembers, startNumber < numMembers>()(d);
   }
};

#define FooSingleCallD(n) \
template <class D> \
struct FooSingleCall<D,n>{ \
   void operator() (D& d) { \
       d.p##n->foo(); \
   } \
}

FooSingleCallD(1);
FooSingleCallD(2);
FooSingleCallD(3);
FooSingleCallD(4);
// ... unfortunately repeat as many times as needed

template <class D, size_t numMembers>
void callFoo(D& d)
{
   FooCaller<D, 1, numMembers, 1 <= numMembers>()(d);
}

注意: 何百もの FooSingleCall を定義しないように賢くしてください... この有名な質問への回答の 1 つを参照してください: https://stackoverflow.com/a/4581720/1463922と、わずか 5 行で 1000 個のインスタンスを作成する方法がわかります。 ...

また、FooSingleCall を、クラスから N ポインターを取得するもの (GetNPointer など) に置き換えてください。

于 2012-07-08T20:14:38.933 に答える
-1

すべてのポインターは同じサイズであり(C ++のすべてのクラスオブジェクトポインターは同じサイズです)、実際には、このポインターのリストのどこかにパディングを挿入するには、コンパイラーをかなり逆にする必要があります。とにかく、可能なパディングの問題はCの場合と同じです。したがって、Cの場合とまったく同じように、まったく問題なく実行できます。

void foo( Base const* );

void bar()
{
    // ...
    foo( *(&thingy.p1 + 3) );
}

とても簡単です。

とは言うものの、マシンで生成されたコードを使用しても、設計はひどい、間違った、本当に本当に悪いように聞こえます。

vtablesを生成するときにそのようなものがあります(各ポインターは一般的に異なる署名の関数を指します)が、それは非常にまれです。したがって、これはXY問題のように聞こえます。たとえば、あなたは問題Xを解決しようとしていて、悪い解決策Yを考え出し、実際の問題Xではなく想像上の解決策Yについて質問しています…

于 2012-07-08T19:33:15.913 に答える