【问题标题】:C++, dealing with multiple constructor overloads and redundant codeC++,处理多个构造函数重载和冗余代码
【发布时间】:2018-05-27 18:24:55
【问题描述】:

我最近对(重新)学习编程产生了兴趣,因此我选择了 C++,因为它是一种常用语言。但是,我遇到了障碍,我怀疑我的解决方案是否是解决它的最佳方法。 我有一个相对复杂的类(无论如何对我来说),有大约 20 个变量,为了简化,它们被分为 4 组。它还有一个在对象初始化期间调用的父类。

但是,我不需要在所有对象中将它们设置为默认值以外的值,因此我设置了各种不同的构造函数重载来考虑所有可能的组合(总共 8 个构造函数)。因此,为了防止编写重复代码,我编写了一些私有函数,仅在构造函数期间调用,将变量设置为我在创建新对象时分配的值。

这是解决这个问题的最佳方法吗?我也想过将这些变量分组到类或结构中,但感觉这太复杂了,在各种构造函数重载期间调用相关函数时应该可以解决问题。如果这不是最优的,那么解决这个问题的最佳方法是什么?为什么?

我可以对我的问题提供更详细的描述,但它会是一堵相当大的文字墙(我先写了一个,但它太失控了)。提前感谢您的意见。

根据要求,这是类定义(武器)。父类 (Item) 已经定义并按预期工作,所以我不会粘贴它,这样人们就不必阅读大量文本。

武器类定义:

class Weapon: public Item {


public:


    // Default constructor
    Weapon();

    // Full constructor
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short StartEnergy, unsigned short MaxEnergy);

    // Constructor for Weapons without Cooldown System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);

    // Constructor for Weapons without Reload System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);

    // Constuctor for Weapons without Energy System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition);

    // Constructor for Weapons without Cooldown nor Reload System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short MaxMagazine, unsigned short MaxAmmunition, unsigned short CurrentEnergy, unsigned short MaxEnergy);

    // Constructor for Weapons without Cooldown nor Energy System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime, unsigned short MaxMagazine, unsigned short MaxAmmunition);

    // Constructor for Weapons without Reload nor Energy System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short CooldownType, double CooldownDuration, unsigned short CooldownShot, double CooldownPeriod, unsigned short MaxMagazine, unsigned short MaxAmmunition);

    // Constructor for Weapons without Cooldown, Reload nor Energy System
    Weapon(unsigned GenericID, bool NameFlag, double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short maxMagazine, unsigned short MaxAmmunition);

    ~Weapon();
    void m_print();

    /*Edited public get and set functions for each variable as they are not relevant*/



private:


    // Ubiquitous variables
    unsigned short WepGenericID = 0;
    unsigned short WepVariantID = 0;
    unsigned short WepSkinID = 0;

    double EquipLoad = 0;
    double EquipLoadperAmmo = 0;

    unsigned short ModesNo = 1;
    Mode* pModes = NULL;

    unsigned short MaxAmmunition = 0;
    unsigned short CurrentAmmunition = 0;

    unsigned short MaxMagazine = 0;
    unsigned short CurrentMagazine = 0;


    // Cooldown System variables
    bool WeaponCooldown = false;
    unsigned short CooldownType = 0;
    double CooldownDuration = 0;
    unsigned short CooldownAction = 0;
    double CooldownPeriod = 0;


    // Reload System variables
    unsigned short ReloadType = 0;
    unsigned short ReloadStyle = 0;
    double ReloadTime = 0;


    // Energy System variables
    unsigned short CurrentEnergy = 0;
    unsigned short MaxEnergy = 0;


    //Constructor Auxiliary Functions
    void m_setGeneralWeapon(double EquipLoad, double EquipLoadperAmmo, unsigned short ModesNo, Mode* pModes, unsigned short MaxMagazine, unsigned short MaxAmmunition);
    void m_setCooldownSystem(unsigned short CooldownType, double CooldownDuration, unsigned short CooldownAction, double CooldownPeriod);
    void m_setReloadSystem(unsigned short ReloadType, unsigned short ReloadStyle, double ReloadTime);
    void m_setEnergySystem(unsigned short StartEnergy, unsigned short MaxEnergy);
    void m_setWeaponIDs();
    void m_WepNameDecisionTree();
    string m_searchName();

};

项目父类定义

class Item {


public:

    Item();
    Item(unsigned GenericID);
    Item(unsigned GenericID, bool NameFlag);
    ~Item();
    void m_setCustomName();


private:

    unsigned GenericID = 0;
    unsigned short GenCategoryID = 0;
    unsigned short GenSubCategoryID = 0;

    bool NameFlag = false;
    string ItemName = "Missingno";

    unsigned long InstanceID = 0;

};

【问题讨论】:

  • 欢迎来到堆栈溢出。向我们展示“您的解决方案”:即代码。故事是在描述复杂的事物,没有代码还不能完全清楚。
  • 你为什么首先使用一个类?
  • 你的方法听起来不错,虽然很高兴看到代码(例如通过pastebin)。在你让代码工作之后,下一个最好的事情是可读性,这导致maintainability
  • 请不要为此使用 pastebin。任何与问题相关的代码都应该在问题本身中。没有什么比在发布问题多年后才发现讨论的主要部分现在隐藏在死链接后面更令人气愤的了。
  • 根据要求,我在原帖中添加了类定义。谢谢你的美言。

标签: c++ class object constructor constructor-overloading


【解决方案1】:

为您的子系统创建单独的类。

使用制造商/工厂模式创建您的武器:

您也可以将弹药分开,只留下几个实际成员。通过这种方式,您可以使用更加模块化的方法构建所有内容,让您可以更轻松地扩展或修改您的功能

【讨论】:

  • 感谢您的建议。我一直在尝试理解这种模式,尽管我认为我没有掌握它是如何工作的,但我一直在尝试使其适应我的代码部分。但是,有一些我无法弄清楚的东西,那就是 Weapon 从其父类 Item 的继承。我怎么解决这个问题?将属于 Item 类的每个变量都设置为虚拟?我会将 Item 类的定义添加到 OP 中,以防万一。提前谢谢你。
  • @S.Marques 抱歉回答迟了。没有虚变量,只有虚函数。我认为您需要“受保护:”部分中的成员变量。任何子类都可以看到它们。
【解决方案2】:

我看到的Weapon API 的一个问题是构造函数采用很多 松散类型的参数,这使得使用它们的代码难以理解和验证。例如,假设您(或您的开发伙伴)使用您的构造函数 API 将这一行添加到您的代码库:

Weapon bfg(id, true, 3.0, 5.0, 6, modesPtr, COOLDOWN_QUICK, 5.0, 3, 4, RELOAD_SLOW, RELOAD_ANYTIME, 3.8, 12, 14);

阅读该行,很难理解大多数数字的含义。如果一个参数被意外省略,或者两个参数的顺序被颠倒了,那么(无论是你还是编译器)都可能不明显那里有错误;相反,在某些游戏测试人员(或客户?)提交错误报告之前,您可能不会发现错误,这是一种发现错误的昂贵且耗时的方法。

因此,我建议将必须传递给构造函数的参数数量减少到尽可能少。例如,上面的另一种写法可能是这样的:

Weapon bfg(id, true);
bfg.SetEquipLoad(3.0);
bfg.SetEquipLoadPerAmmo(5.0);
bfg.SetModes(6, modesPtr);
bfg.SetCooldownType(COOLDOWN_QUICK);
[...]

诚然,这更冗长(而且它确实可能忘记设置你应该设置的东西),但至少当你看它时,很明显3.0 适用于EquipLoad 设置5.0 适用于 EquipLoadPerAmmo 设置,而不是相反。也就是说,您不必不断地在 .h 文件和代码之间来回查看来尝试找出每个值所指的内容。

请注意,每个设置方法都应采用产生有用结果所需的所有参数;因此,例如,如果指定 EquipLoad 而不指定 EquipLoadPerAmmo 没有任何意义,那么您最好通过一次调用来设置这两个:

bfg.SetEquipLoadAndEquipLoadPerAmmo(3.0, 5.0);

... 这样编码员​​就不可能(即编译时错误)错误地设置一个而忽略设置另一个。

至于处理冗长以最小化冗余代码,下一步是将上述代码包装在一个函数中,这样对于任何给定的武器类型,只有一个地方可以创建它,例如:

Weapon MakeBFG(unsigned id)
{
   Weapon bfg(id, true);
   bfg.SetEquipLoad(3.0);
   bfg.SetEquipLoadPerAmmo(5.0);
   bfg.SetModes(6, modesPtr);
   bfg.SetCooldownType(COOLDOWN_QUICK);
   [...]
   return bfg;
}

现在,您的其余代码可以在想要创建新的 BFG 枪时调用 Weapon bfg = MakeBFG(idCounter++);

除此之外,我同意另一张海报的观点——您的班级似乎正在处理许多不同的事情,如果您能找到一种方法将其分解为多个较小的班级(并非所有班级都需要公开通过公共 API;私有类很棒),这可能会帮助您管理代码的整体复杂性;如果您认为将来要继续添加新功能/行为,这样做尤其有益,因为如果您将所有内容放在一个类中,那么当您尝试制作时,该类的复杂性将很快失控它支持越来越多不同的行为/用例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-14
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    相关资源
    最近更新 更多