2016-12-01 35 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 –

相关问题