2016-12-01 40 views
2

我有一些類描述能力/行爲,如飛行或駕駛等。每個類都有一個特定的方法,必須調用來加載一些數據 - 例如,Flyable有loadFlyData(),Drivable有loadDriveData()。對於每個類,方法名稱都是唯一的。如何從基類派生的子類自動調用方法或生成代碼?

我有許多派生類可能從一個或多個這些行爲類繼承。每個派生類都有一個名爲loadData()的方法,其中我們應該調用所有的父行爲類方法,如loadFlyData(),loadDriveData()等....有沒有一種方法可以使用元編程自動生成此方法?由於有許多派生類,如果我可以使用元編程生成這些方法,它可能更易於維護...

行爲類:(一個對象類可能有這些行爲中的任何一種,並且必須調用那些類「load 「方法...

class Flyable { 
    void loadFlyData() { 
    } 
}; 

class Drivable{ 
    void loadDriveData() { 
    } 
}; 

所有對象類從對象導出:

class Object { 
    virtual void loadData() { 
    } 
}; 

派生類:

class FlyingCar : public Object, public Flyable, public Drivable { 
    virtual void loadData() override { 
     // How to automatically generate code so that the next two lines are called: 
     loadFlyData(); 
     loadDriveData(); 
    } 
}; 
+0

可以'loadFlyData()'和'loadDriveData'是同一個名字的?這樣它們只能通過範圍來區分,「Flyable :: loadData()'和'Drivable :: loadData()'?如果是這樣,那麼基於模板的方法來調用每個T :: loadData(),其中T是一個基類,應該可以工作。雖然不確定如何以可變參數的形式獲取基類列表。 – themagicalyang

+0

如果所有的基礎對象對「加載」函數使用相同的名稱(例如'loadData'),那麼可以創建一個預處理步驟來獲取源文件並對其進行修改。使用模板元編程可能是可能的,但它可能比它的價值更多的工作(並且你現在的解決方案將更少寫,更重要的是更易於閱讀和維護)。在運行時也可以創建代碼,但這會更困難,並且會使其不易讀和維護。你現在擁有的只是最好和最簡單的解決方案。 –

+0

是的,我們可以爲loadFlyData和loadDriveData命名。對我來說(來自java背景)的困惑是如果他們有相同的名稱(這就是爲什麼我使用不同的名稱)如何處理衝突。下面給出的答案是我尋找的東西(由講故事者和krzaq)。我不知道你可以在C++中做這些事情! –

回答

3

,您可以撥打免費功能loadXData(),這將成爲一個空操作,如果你的類不X

namespace detail 
{ 

void loadFlyData(Flyable* ptr) { ptr->loadFlyData(); } 
void loadFlyData(...) {} 

void loadDriveData(Drivable* ptr) { ptr->loadDriveData(); } 
void loadDriveData(...) {} 

} 

class FlyingCar : public Object, public Flyable, public Drivable{ 
public: 
    virtual void loadData()override{ 
     //How to automatically generate code so that the next two lines are called: 
     detail::loadFlyData(this); 
     detail::loadDriveData(this); 
    } 
}; 

demo

雖然我想用一個共同的名字loadData,只是稱這是對所有可變親本可能是優選的:

template<typename... Policies> 
struct ComposedType : Object, Policies... 
{ 
    virtual void loadData() override { 
     int arr[] = { 
      ((void)Policies::loadData(), 0)... 
     }; 
     (void)arr; 
    } 
}; 

using FlyingCar = ComposedType<Drivable, Flyable>; 

demo

上面loadData可以在C++ 1Z被簡化:

virtual void loadData() override { 
    ((void)Policies::loadData(), ...); 
} 

demo

+1

'((void)Policies :: loadData(),0)...'並且不需要修改返回類型。我今天學到了東西:) – StoryTeller

+0

什麼是「(void)arr;」爲?它有什麼作用 ?另外,如果飛行汽車有額外的行爲,我想補充呢?在我看來,我應該實際上保留最派生的類,因爲它沒有真正具有它自己的任何行爲(只是使用語句),而是將其組合成其他行爲...... –

+0

另外,爲什麼你有0 ,在((voidPoliciens :: loadData(),0)...? –

8

當然可以。然而,您需要使用一些約定,以便代碼可以是通用的。 See it live

#include <iostream> 
using namespace std; 

struct Flyable{ 
    int loadConcreteData(){ 
    cout << "Flyable\n"; return 0; 
    } 
}; 

struct Drivable{ 
    int loadConcreteData(){ 
    cout << "Drivable\n"; return 0; 
    } 
}; 

class Object{ 
    virtual void loadData(){ 
    } 
}; 

template<class ...CS> 
struct ConcreteLoader : Object, CS... { 
    void loadData() override { 
     int load[] = { 
      this->CS::loadConcreteData()... 
     }; 
    } 
}; 

class FlyingCar : public ConcreteLoader<Flyable,Drivable>{ 
}; 

int main() { 
    FlyingCar fc; 
    fc.loadData(); 
    return 0; 
} 

的變化是需要提的是:

  1. 每個具體Load函數的返回類型,就必須改變。這是爲了便於擴展參數包中的「數組技巧」。
  2. 所有加載函數的名稱都是相同的,同樣的原因。

一旦C++ 17和摺疊表達式展開,原因(1)可能會過時。

+0

我會建議提供的代碼,至少編譯,你的方法完成返回它.. –

+1

@OlegBogdanov,哦,它不編譯?我想現場的例子是一個謊言 – StoryTeller

+0

好吧,對不起,它編譯,但有一些UB –

相關問題