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<BaseUnit*>& MyVector
- IMO,它不应该是您传递的参数,而是您的游戏类的本地成员,您可以直接访问它而无需将其作为参数。
另外,请遵循正确的编码约定,不要使用大写的成员方法或对象或参数。这只是令人困惑。
【讨论】:
以上是关于c ++将不同的类传递给函数的主要内容,如果未能解决你的问题,请参考以下文章
dtrace - 从调用函数的不同位置传递给函数的变量的聚合