簡體   English   中英

C ++:將結構成員視為數組

[英]C++: treating structure members as an array

我有一個包含很多(如數百個)指針的結構。 每個指針都是不同的類型,但是它們都繼承自一個通用的基類---我們稱之為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...
}

問題是,我有一個十億不同類型的數據的很多很多的這些,和上面的代碼最終被非常大,影響到我的足跡。 所以我正在嘗試用更智能的東西代替它。

在C語言中,我可以簡單地獲取第一個結構成員的地址和計數,然后執行以下操作:

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

但是,當然,我不能用C ++做到這一點,因為結構成員不是統一類型,將它們強制轉換為Base *可能涉及更改它們,並且可能涉及更改指針,並且因為它們不是統一類型的指針將必須針對每個成員進行不同的更改,而我不能這樣做。

有沒有辦法做這樣的事情是C ++? 這全是機器生成的代碼,所以不必太漂亮了,值得慶幸的是,鯊魚已經被捉住了,但是我確實需要它盡可能地可移植...

(我確實有權使用RTTI,但出於性能原因,如果可能,請避免使用它。)

更新:

因此,據我所知...您只是無法在C ++中做到這一點。 根本無法完成。 我對它在C語言中完全簡單的事實感到迷惑。在C ++中,如果您要有一個類型良好的指針開始,則只能在類層次結構中的兩種類型之間安全地轉換指針,當然,我沒有。

因此,我將不得不更改底層問題以避免這種情況:我想出的解決方案是將結構中的每個指針存儲兩次,一次存儲在用於迭代的Base *數組中,一次存儲為Class *用於調用方法。 當然,這很爛,因為它將使Data結構的大小增加一倍。

因此,如果有人想確認這一點(我希望被證明是錯誤的),我會很樂意將他們的答案標記為正確的……

每個指針都是不同的類型,但是它們都繼承自一個通用的基類---我們稱之為Base

而不是擁有數百個成員,只需保留一個Base類指針容器:

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

在一個好的設計中,您實際上並不需要知道每個對象的類型,如果將其抽象化,則實際上會更好。 記住,針對接口而不是具體的類進行編程總是好的。

並將其投放到Base *可能涉及更改它們

並非如此,對於public繼承,演員表應該是隱式的。

如果只對它們全部調用Foo() ,則Foo()可以是基類中的virtual方法,這樣您就可以充分利用多態性。

但是,當然,我不能用C ++做到這一點,因為結構成員不是統一類型,將它們強制轉換為Base *可能涉及更改它們,並且可能涉及更改指針,並且因為它們不是統一類型的指針將必須針對每個成員進行不同的更改,而我不能這樣做。

盡管這是一個壞主意,但事實並非如此:您可以像在C語言中那樣使用指針算法對指針進行迭代。指針的大小均相同,並且它們具有相同的基類(不考慮結構對齊問題等)。 它很臟,但在技術上卻可以。 如果您將實例本身作為類的成員而不是指向它們的指針,那將是不同的。

C ++ 11中最好的方法是使用

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

參見http://www.drdobbs.com/cpp/c11-uniqueptr/240002708

您還必須通過自動生成代碼來與自動生成的代碼“抗衡”。 您可以使用ctags之類的工具來“解析”您自動生成的類,並從ctags輸出中自動生成代碼。 參見http://ctags.sourceforge.net/ctags.html

您還可以按照以下可能的重復問題中的建議將數據強制轉換為元組: 遍歷struct變量

我不確定哪個更快...

如果您可以修改自動生成此源代碼的工具-也許最好是擴展此工具...

另一個選項(不可編譯):

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;
};

我不想更改以前的答案。 但是,我提出了另一種適合您的解決方案:此處的完整示例: 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 ...請參閱此著名問題的答案之一: https ://stackoverflow.com/a/4581720/1463922,您將看到如何僅用5行就可以制作1000個實例...

另外,請用從類中檢索N指針的東西替換FooSingleCall,例如GetNPointer ...

所有指針的大小都相同(C ++中的所有類對象指針的大小都相同),實際上,編譯器需要相當不正確,以便在此指針列表的任何位置插入填充。 無論如何,可能的填充問題與C中相同。因此,您可以執行與C中相同的操作,完全沒有問題。

void foo( Base const* );

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

就這么簡單。

就是說,即使使用機器生成的代碼,設計聽起來也很可怕,錯誤,真的很糟糕。

生成vtable的時候確實有這種類型的東西(每個指針指向通常具有不同簽名的函數),但這是非常罕見的。 因此,這聽起來像是XY問題。 例如,您正在嘗試解決問題X,提出了一個不好的解決方案Y,現在又在詢問想象中的解決方案Y,而不是真正的問題X…

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM