c ++将不同的类传递给函数

Posted

技术标签:

【中文标题】c ++将不同的类传递给函数【英文标题】:c++ pass different classes into function 【发布时间】:2015-04-11 20:56:19 【问题描述】:

我可能完全走错了路,但已经很晚了,这让我有点生气。

我的基类中有一个函数,应该允许我创建派生类的新对象。但是,在它说 Melee 的地方,我希望它可以与我的其他派生类互换。我不知道如何传入不同的类,因此它将创建该类的对象。

void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector)

    UnitID++;
    MyVector.push_back(new Melee());


【问题讨论】:

需要在编译时还是运行时确定? 运行时,他们会按一个键,它会相应地创建一个不同的单元 您需要创建某种 switch 语句或替代方法来将输入转换为类型。考虑不要为每个单位创建一个不同的类,而是一个包含近战力量、远程力量、显示图形等成员的单位类 【参考方案1】:

您可以使用“prototype design pattern”。

输入std::map 关联键 -> 原型,您就完成了:当用户按下一个键时,您查找地图,提取原型并将其传递给CreateNewUnit。在 CreateNewUnit 中,您将使用 Prototype::Clone()

class BaseUnit

public:
    ... // your methods
    virtual BaseUnit* Clone() = 0;
;

class Melee : public BaseUnit

public:
    BaseUnit* Clone()  return new Melee(); 
;

void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, BaseUnit* prototype )

    UnitID++;
    MyVector.push_back( prototype -> Clone() );


int main()

    std::map< char, BaseUnit* > prototypes;
    prototypes[ 'a' ] = new Melee();
    prototypes[ 'b' ] = new OtherUnit();
    ...

    std::vector< BaseUnit* > units;

    char selection = GetKeyPressed();
    BaseUnit::CreateNewUnit( units, prototypes[ selection ] );

此解决方案的好处是,当您需要在应用程序中添加新的单元类型时,您只需在原型映射中添加一个新条目即可。

顺便说一句:如果从 BaseUnit 派生的类在全局“原型”映射中注册自己,您甚至可以通过一点努力来避免这种情况。

【讨论】:

我宁愿存储一个指向返回新对象类型的函数的指针映射,而不是要求可以复制对象。 @NeilKirk 我的代码不需要可以复制对象。 @Daniele - 但它需要基本上未使用的对象实例,只是为了在它们上调用虚拟方法。不幸的是,你不能拥有静态虚拟,所以我会说函数指针映射是一个更好的解决方案。它确实违背了目的,映射将引入开销并且不会增加额外的灵活性,而使用函数指针,您可以从动态库中注册新类型。 @NeilKirk 好吧,是的,如果你的对象是重量级的,也许 std::function 的映射可能会更好(但没有函数指针,请:-)。理想情况下,我想要一个模板工厂函数,这样我就不需要为每个具体类定义一个函数。 @Daniele - 与现代主义福音传道者可能会说的相反,好的旧的原始指针并没有错。几十年来它们都很好,现在仍然如此。但是,如果它让你感觉更好,把它们放在一个薄的包装纸里——把你自己弄晕了。就我个人而言,我认为人们忽视了最近的 C++ 特性背后的意图,并以某种时尚的方式使用它们,即使在它们适得其反的情况下也是如此。【参考方案2】:

我要么传递一个指向单元的指针:

void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, BaseUnit* unit)

    UnitID++;
    MyVector.push_back(unit);

或传递按下的键或其他一些 id:

void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, char type)

    switch(type) //this would be the key pressed
    
    case '1':
         MyVector.push_back(new Melee());
         UnitID++;
         break;//this would be continued for all possible units
    

【讨论】:

关于第二种解决方案,我总是想创建一个工厂函数数组(或map)而不是switch语句。【参考方案3】:

如果您希望在编译时确定它,您可以使用模板方法或为每个类创建一个方法。这称为工厂方法,它是一种设计模式。请注意,此处对工厂的“提示”是模板参数。

如果您希望在运行时确定它,则需要以某种方式识别相关类,即工厂的“提示”。大多数情况下,这将是一个枚举或字符串。再次阅读工厂设计模式和工厂方法。

【讨论】:

【参考方案4】:

这可能会让您知道如何做到这一点: 1. 更改函数签名以包含派生对象指针作为第二个参数,

void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, const BaseUnit* derived) 
    UnitID++;
    MyVector.push_back(derived);

    按键时调用上述函数,如果按键被按下,则创建一个新的派生对象并调用你的函数:

    近战 *m = 新近战();

    unit.CreateNewUnit(myVector,m);

【讨论】:

这将在堆栈上创建一个Melee 单元,将其注册到向量中,并在函数返回后产生一个悬空指针,从而导致未定义的行为并很可能崩溃。【参考方案5】:

如果您在编译时需要它,您可以简单地使用工厂模板。如果不同的类有不同的参数需要传递,你应该使用可变参数模板:

#include <utility> 

class Factory 
    ...
    template <typename Derived, typename ...Args>
    BaseUnit * create(Args &&... args)
     
        return new Derived(std::forward<Args>(args)...);
    
;

然后您可以简单地创建一个工厂并调用factory.create(Melee, optional_arguments) 或任何类型,然后将创建一个模板特化来使用提供的参数实例化该特定类型。

如果您需要“运行时调度”创建函数,您可以在BaseUnit 中使用enum Type 或列出所有可能派生类型的其他ID,并使用create(BaseUnit::Type type) 方法和switch (type)语句和每个创建适当类型的对象类型的案例。当然,这可以与上面列出的模板工厂结合使用。

switch 语句将比 map 更有效,此外,如果 map 是“静态”填充的,则它不会真正有意义。仅当您想在运行时注册新的对象创建方法时,映射才有意义。在这种情况下,您可以拥有一个类型 id 的映射和一个指向函数的指针,返回一个新的 BaseType 派生。例如,您可以通过这种方式注册从动态库加载的额外函数,根据类型 id 查找函数指针并使用它来实例化该类型的对象。但这只是在您需要运行时注册的情况下。否则坚持以前的解决方案。仅当您拥有诸如“插件”之类的东西时才合理-动态库提供了一组额外的类型和适当的函数来创建它们,因此您可以加载库,在映射中注册其组件并能够实例化在游戏“核心逻辑”的实现过程中不存在的类型。自然,这一切都必须依赖于核心逻辑设计的多态接口才能工作,否则就不会。此外,对于这个解决方案,您可能希望 id 是字符串而不是整数,因为它提供了更大的灵活性并且没有 switch 语句的限制。

另外请注意,我没有包含您的 vector&lt;BaseUnit*&gt;&amp; MyVector - IMO,它不应该是您传递的参数,而是您的游戏类的本地成员,您可以直接访问它而无需将其作为参数。

另外,请遵循正确的编码约定,不要使用大写的成员方法或对象或参数。这只是令人困惑。

【讨论】:

以上是关于c ++将不同的类传递给函数的主要内容,如果未能解决你的问题,请参考以下文章

将数组和数组指针传递给C中的函数之间的区别

将不同类型的向量传递给模板函数

如何将不同的类传递给元素指令

dtrace - 从调用函数的不同位置传递给函数的变量的聚合

使用 CreateThreadpoolWork/SubmitThreadpoolWork 时将不同的值传递给回调函数

指针数组内容在传递给 C 中的函数时被擦除