【问题标题】:How can I fully reassign an instance of a class to another instance of that same class (then delete the original object) in C++?如何在 C++ 中将一个类的实例完全重新分配给同一类的另一个实例(然后删除原始对象)?
【发布时间】:2020-01-15 23:08:10
【问题描述】:

我正在通过制作控制台游戏来玩弄 C++。我有一个名为“WeaponType”的超类。在这个类中有一个静态方法,可以创建我需要的所有类型的武器。子类“Weapon”将继承自 WeaponType。 (我是继承新手,请提前原谅我的无知。)

我现在看到代码的方式是,每次创建 WeaponType 子类的实例时,我都必须声明一个新的 WeaponType 超类实例。这是一个问题,因为我不想再创建在创建它们的静态方法中在 HEAP 上初始化的 WeaponType 超类的任何实例。

我正在寻找一种方法来删除 WeaponType 超类的每个新实例,然后再对对象执行我需要的操作。 (例如,如果创建另一个 WeaponType 实例,指定它应该是“melee”对象,并且我已经创建了一个名为“melee”的实例,我希望能够将 WeaponType“迁移”到原始对象并删除新对象)。

我的代码不完整,但你应该能明白我想要什么。

WeaponType 的大部分代码如下:

<!-- WeaponType.h -->
#pragma once

#include<iostream>
#include<string>
#include<vector>
#include<map>

class WeaponType
{
private:
    static std::map<int, WeaponType*> weapTypeAddresses;

    /* Private setters */
    static void setWeapTypeAddresses(const std::vector<WeaponType*>&);

protected:
    double reloadSpeed;
    int weapTypeID, ammoPerShot, maxAmmo;
    std::string weapTypeName;

public:
    /* Constructors */
    WeaponType(int, double, int, int);
    WeaponType(int);
    WeaponType();
    static void MakeWeaponTypes();

    /* Destructor */
    ~WeaponType();

    /* Converters */
    static int convTo_WeapTypeID(const std::string&);

    /* Getters */
    double getReloadSpeed() const;
    int getWeapTypeID() const;
    int getAmmoPerShot()const;
    int getMaxAmmo() const; 
    std::string getWeapTypeName() const;
    static WeaponType* getWeapTypeAddress(int);
};
<!-- WeaponType.cpp -->
#include "WeaponType.h"

/* Global Variables */
enum WeaponClasses {
    meleeE, pistolE, machineGunE, shotgunE, sniperRifleE, explosiveE,
    WeaponClasses_SizeE = explosiveE + 1
};

std::vector<std::string> weaponClassNames = 
    { "melee", "pistol", "machineGun", "shotgun", "sniperRifle", "explosive" };

enum reloadSpeeds
{
    meleeRS = 0, pistolRS = 2, machineGunRS = 6,
    shotgunRS = 5, sniperRifleRS = 7, explosiveRS = 30
};

enum ammoPerShots 
{
    meleeAPS = 0, pistolAPS = 1, machineGunAPS = 4,
    shotgunAPS = 2, sniperRifleAPS = 1, explosiveAPS = 1
};

enum maxAmmo
{
    meleeMA = 1, pistolMA = 15, machineGunMA = 50,
    shotgunMA = 20, sniperRifleMA = 10, explosiveMA = 3
};

bool weaponsMade = false;


/*******************
 * Private Setters *
 *******************/

// Maps weapTypeID to its pointer
void WeaponType::setWeapTypeAddresses (const std::vector<WeaponType*>& weapTypePtrs)
{
    weapTypeAddresses[meleeE]       = weapTypePtrs[meleeE];
    weapTypeAddresses[pistolE]      = weapTypePtrs[pistolE];
    weapTypeAddresses[shotgunE]     = weapTypePtrs[shotgunE];
    weapTypeAddresses[machineGunE]  = weapTypePtrs[machineGunE];
    weapTypeAddresses[sniperRifleE] = weapTypePtrs[sniperRifleE];
    weapTypeAddresses[explosiveE]   = weapTypePtrs[explosiveE];
}


/****************
 * Constructors *
 ****************/

WeaponType::WeaponType
    (int weapTypeID, double reloadSpeed, int ammoPerShot, int maxAmmo) :
    reloadSpeed(reloadSpeed), ammoPerShot(ammoPerShot), maxAmmo(maxAmmo)
{
    // Makes sure weapTypeID is a valid int within WeaponClasses
    try
    {
        if (weapTypeID >= 0 && weapTypeID < WeaponClasses_SizeE) { this->weapTypeID = weapTypeID; }
        else { throw weapTypeID; }
    }
    catch (int e)
    {
        std::cerr << weapTypeID << " is not a valid weapTypeID" << std::endl;
        std::cerr << "Setting weapTypeID to 0" << std::endl;
        this->weapTypeID = meleeE;
    }

    weapTypeName = weaponClassNames[weapTypeID];
}

/* This is the important constructor that needs to delete the object
that it's constructing */
WeaponType::WeaponType(int weapClassID)
{
    try
    {
        // I need to delete the object that's being created somewhere in here
        switch (weapClassID)
        {
        case meleeE:
            *this = *WeaponType::getWeapTypeAddress(meleeE);
            break;

        case pistolE:
            *this = *WeaponType::getWeapTypeAddress(pistolE);
            break;

        case machineGunE:
            *this = *WeaponType::getWeapTypeAddress(machineGunE);
            break;

        case shotgunE:
            *this = *WeaponType::getWeapTypeAddress(shotgunE);
            break;

        case sniperRifleE:
            *this = *WeaponType::getWeapTypeAddress(sniperRifleE);
            break;

        case explosiveE:
            *this = *WeaponType::getWeapTypeAddress(explosiveE);
            break;

        default:
            throw weapClassID;
            break;
        }
    }
    catch (int e)
    {
        std::cerr
            << '\"' << e << "\" is not a valid "
            << "weapClassID in WeaponType::WeapType(int)"
            << std::endl;
        std::cerr << "Calling destructor" << std::endl;
        delete this;
    }
}

// Empty constructor for reasignment
WeaponType::WeaponType() {}

void WeaponType::MakeWeaponTypes()
{
    if (!weaponsMade)
    {
        WeaponType * melee       = new WeaponType( meleeE,       meleeRS,       meleeAPS,       meleeMA       ),
                   * pistol      = new WeaponType( pistolE,      pistolRS,      pistolAPS,      pistolMA      ),
                   * machineGun  = new WeaponType( machineGunE,  machineGunRS,  machineGunAPS,  machineGunMA  ),
                   * shotgun     = new WeaponType( shotgunE,     shotgunRS,     shotgunAPS,     shotgunMA     ),
                   * sniperRifle = new WeaponType( sniperRifleE, sniperRifleRS, sniperRifleAPS, sniperRifleMA ),
                   * explosive   = new WeaponType( explosiveE,   explosiveRS,   explosiveAPS,   explosiveMA   );

        // Sets all the pointers to weapTypeAddresses map
        std::vector<WeaponType*> weapTypePtrs =
        {
            melee,   pistol,      machineGun,
            shotgun, sniperRifle, explosive
        };
        WeaponType::setWeapTypeAddresses(weapTypePtrs);

        weaponsMade = true;
    }
}


/*****************
 * Other Methods *
 *****************/

/* Destructor */
WeaponType::~WeaponType()
{
    std::cout << "WeaponTypeID \"" << this->weapTypeID << "\" is being deleted" << std::endl;
}

/**************
 * Converters *
 **************/

// Converts weapTypeName to weapTypeID
int WeaponType::convTo_WeapTypeID(const std::string& className)
{
    WeaponClasses weapClass;

    // Makes sure className is valid
    try
    {
        int errorCounter = 0, errorMax = 0;
        for (int i = 0; i < WeaponClasses_SizeE; i++)
        {
            if (className != weaponClassNames[i]) { errorCounter++; }
            errorMax++;  // Always increases errorMax by 1
        }

        if (errorCounter == errorMax) { throw 0; }
    }
    catch (int e)
    {
        std::cerr << "className parameter is invalid" << std::endl;
        std::cerr << "Returning \"" << weaponClassNames[0] << "\"" << std::endl;
        return meleeE;
    }

    // Maps the weapTypeName to weapTypeID
    std::map<std::string, WeaponClasses> Weap_ID;
        Weap_ID[weaponClassNames[meleeE]]       = meleeE;
        Weap_ID[weaponClassNames[pistolE]]      = pistolE;
        Weap_ID[weaponClassNames[machineGunE]]  = machineGunE;
        Weap_ID[weaponClassNames[shotgunE]]     = shotgunE;
        Weap_ID[weaponClassNames[sniperRifleE]] = sniperRifleE;
        Weap_ID[weaponClassNames[explosiveE]]   = explosiveE;

    weapClass = Weap_ID[className];
    return weapClass;
}


/***********
 * Getters *
 ***********/

double WeaponType::getReloadSpeed() const { return reloadSpeed; }
int WeaponType::getWeapTypeID() const { return weapTypeID; }
int WeaponType::getAmmoPerShot() const { return ammoPerShot; }
int WeaponType::getMaxAmmo() const { return maxAmmo; }
std::string WeaponType::getWeapTypeName() const { return weapTypeName; }
WeaponType* WeaponType::getWeapTypeAddress(int weapClassID)
{
    // Makes sure weapClassID is valid input
    try
    {
        switch (weapClassID)
        {
        case 0:
            break;

        case 1:
            break;

        case 2:
            break;

        default:
            throw weapClassID;
        }
    }
    catch (int e)
    {
        std::cerr 
            << '\"' << e << "\" is not a valid "
            << "weapClassID in WeaponType::getWeapTypeAddress(int)" 
            << std::endl;
        std::cerr << "Returning " << weapTypeAddresses[meleeE] << std::endl;
        return weapTypeAddresses[meleeE];
    }

    return weapTypeAddresses[weapClassID];
}

从 Weapon.cpp(尚未创建),我会得到如下内容: (同样,请记住我是继承新手,可能有不正确的代码。)

<!-- Pre-file of Weapon.cpp -->
#include "Weapon.h"
#include "WeaponType.h"

Weapon::Weapon(std::string weapName, double damage, int ammo, int weapTypeID) : 
    WeaponType::WeaponType(weapTypeID)
{
    this->damage = damage;
    this->ammo = ammo;
}

最后,一个简单的 main 方法

#include<iostream>
#include<string>
#include<vector>
#include<map>
#include "WeaponType.h"
#include "Weapon.h"
using namespace std;

int main()
{
    string gunName = "pistolGun";
    double gunDamage = 10.0;
    int gunAmmo = 20;
    int weapTypeID = 1; 

    // Creates a pistolGun object.
    Weapon pistolGun (gunGame, gunDamage, gunAmmo, weapTypeID);

    // The goal is to get the WeaponType* of pistolGun to equal &pistol from WeaponType.
    // That would mean these who lines of code output the same address.
    // Also note: I know this code to reference these addresses is incorrect
    cout << "Address of WeaponType attributes of pistolGun: " << &pistolGun.WeaponTypeAttribs << endl;
    cout << "Address of pistol WeaponType: " &pistol << endl;
}

【问题讨论】:

  • 一般来说,组合优于继承。在这种情况下,这意味着 Weapon 不应该从 WeaponType 继承。 WeaponType 应该是 Weapon 的成员。我可能会将其设为 WeaponType 实例的指针或引用,因为它将在所有 Weapon 实例之间共享。
  • @Ayjay 你能写几行代码来举个例子或提供一个链接(或两者都有哈哈)吗?

标签: c++ oop pointers inheritance operator-overloading


【解决方案1】:

据我了解,您使用 Wea​​ponType 来存储所有特征,而 Weapon 代表游戏中的实际对象,因此它有一个类型,但它也有诸如弹药或世界位置之类的东西这个。

不要将此关系建模为 Weapon is-a WeaponType,而是将其建模为 Weapon has-a WeaponType。将链接代码减少到合理的程度,这样的方法可能会更好:

#include<string>
#include<memory>

class WeaponType
{
public:
    double reloadSpeed;
    int weapTypeID, ammoPerShot, maxAmmo;
    std::string weapTypeName;
};

class Weapon
{
public:
    explicit Weapon(std::shared_ptr<const WeaponType> type)
        : type(std::move(type))
    {
        ammo = this->type->maxAmmo;
    }

    const std::shared_ptr<const WeaponType> type;
    int ammo;
};

int main()
{
    // make the weapon types
    auto pistolType     = std::make_shared<WeaponType>(WeaponType{ 1.0, 0, 1, 20, "pistol" });
    auto machineGunType = std::make_shared<WeaponType>(WeaponType{ 5.0, 1, 1, 100, "machine gun" });
    // ...etc

    // Create some actual weapon objects out of those types
    auto pistolGun1 = Weapon{ pistolType };
    auto pistolGun2 = Weapon{ pistolType };

    auto machineGun1 = Weapon{ machineGunType };
    auto machineGun2 = Weapon{ machineGunType };
}

【讨论】:

    猜你喜欢
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-16
    • 1970-01-01
    • 2023-01-25
    相关资源
    最近更新 更多