Home > Software engineering >  c : arithmetic operator overload
c : arithmetic operator overload

Time:05-27

I have a class called HealthPoints that has hp and max_hp members. I want to overload the operator so it will work like this:

HealthPoints healthPoints1;
healthPoints1 -= 150; /* healthPoints1 now has 0 points out of 100 */
HealthPoints healthPoints2(150);
healthPoints2 -= 160; /* healthPoints2 now has 0 points out of 150 */
healthPoints2 = healthPoints1   160; /* healthPoints2 now has 100 points out of 100 */

I succeeded in making it work, but only in a specific order. If I put the number before the object, is there any way to make it recognize the number between the two of them and act accordingly? The way it works at the moment, I get that healthPoints2 now has 160 points out of 160.

HealthPoints class:

class HealthPoints {
    public:
        static const int DEFAULT_MAXHP=100;
        static const int DEFAULT_HP=100;
        static const int MINIMUM_MAXHP=1;
        static const int MINIMUM_HP=0;
        explicit operator int() const;
        HealthPoints(int maxHP=HealthPoints::DEFAULT_MAXHP);
        ~HealthPoints() = default; // Destructor set to default
        HealthPoints(const HealthPoints&) = default; // Copy Constructor set to default
        HealthPoints& operator=(const HealthPoints&) = default; // Assignment operator set to default
        HealthPoints& operator =(const HealthPoints& HP);
        HealthPoints& operator-=(const HealthPoints& HP);
        class InvalidArgument {};
    private:
        int m_maxHP;
        int m_HP;
        friend bool operator==(const HealthPoints& HP1, const HealthPoints& HP2);
        friend bool operator<(const HealthPoints& HP1, const HealthPoints& HP2);
        friend std::ostream& operator<<(std::ostream& os, const HealthPoints& HP);
};

HealthPoints::HealthPoints(int maxHP) {
    if(maxHP < HealthPoints::MINIMUM_MAXHP) {
        throw HealthPoints::InvalidArgument();
    }
    this->m_maxHP=maxHP;
    this->m_HP=m_maxHP;
}

HealthPoints operator (const HealthPoints& HP1, const HealthPoints& HP2) {
    return HealthPoints(HP2) =HP1;
}

HealthPoints operator-(const HealthPoints& HP1, const HealthPoints& HP2) {
    return HealthPoints(HP1)-=HP2;
}

HealthPoints& HealthPoints::operator =(const HealthPoints& HP) {
    if(this->m_HP HP.m_HP>this->m_maxHP) {
        this->m_HP=this->m_maxHP;
    } else {
        this->m_HP =HP.m_HP;
    }
    return *this;
}

HealthPoints& HealthPoints::operator-=(const HealthPoints& HP) {
    if(this->m_HP-HP.m_HP>HealthPoints::MINIMUM_HP) {
        this->m_HP-=HP.m_HP;
    } else {
        this->m_HP=HealthPoints::MINIMUM_HP;
    }
    return *this;
}

CodePudding user response:

In this statement:

healthPoints2 = healthPoints1 160;

Your operator is creating a new HealthPoints object that is a copy of healthPoints1, thus the object's m_maxHP is set to 100. Then you are adding 160 to that object, increasing its m_HP but preserving its m_maxHP. Then you are assigning that object to healthPoints2, copying both m_HP and m_maxHP values. That is why the m_maxHP of healthPoints2 changes from 150 to 100.

In this statement:

healthPoints2 = 160 healthPoints1;

Your operator is creating a new HealthPoints object whose m_maxHP is set to 160. Then you are adding healthPoints1 to that object, increasing its m_HP but preserving its m_maxHP. Then you are assigning that object to healthPoints2, copying both m_HP and m_maxHP values. That is why the m_maxHP of healthPoints2 changes from 150 to 160.

If you want your operators to update only the m_HP and not change the m_maxHP, you need to add overloads that accept int values instead of HealthPoints objects, eg:

HealthPoints& HealthPoints::operator =(int value) {
    m_HP = std::min(m_HP   value, m_maxHP);
    return *this;
}

HealthPoints& HealthPoints::operator-=(int value) {
    m_HP = std::max(m_HP - value, HealthPoints::MINIMUM_HP);
    return *this;
}

HealthPoints operator (const HealthPoints& HP, int value) {
    return HealthPoints(HP)  = value;
}

HealthPoints operator (int value, const HealthPoints& HP) {
    return HealthPoints(HP)  = value;
}

HealthPoints operator-(const HealthPoints& HP, int value) {
    return HealthPoints(HP) -= value;
}

HealthPoints operator-(int value, const HealthPoints& HP) {
    return HealthPoints(HP) -= value;
}

And then, mark the HealthPoints(int) constructor as explicit to avoid unwanted implicit conversions when passing int values to parameters that are expecting HealthPoints objects.

CodePudding user response:

I'm not completely sure of the logic of your HealthPoints class, so double check my code below. My point, though, is to implement:

    friend HealthPoints operator (const HealthPoints& other, int value);
    friend HealthPoints operator (int value, const HealthPoints& other);

You actually just need to implement the first one, and have the second one use it:

HealthPoints operator (const HealthPoints& hp, int value) {
    HealthPoints ret{hp};
    ret.m_HP = std::min(ret.m_maxHP, ret.m_HP   value);
    return ret;
}
HealthPoints operator (int value, const HealthPoints& hp) {
    return hp   value;
}

You can see it here in action:

[Demo]

int main() {
    HealthPoints hp1{100};
    hp1 -= 50;
    std::cout << "hp1: " << hp1 << "\n";
    auto hp2{hp1   10};
    std::cout << "hp2: " << hp2 << "\n";
    auto hp3{10   hp2};
    std::cout << "hp3: " << hp3 << "\n";
}

// Outputs:
//   hp1: (max_HP = 100, HP = 50)
//   hp2: (max_HP = 100, HP = 60)
//   hp3: (max_HP = 100, HP = 70)
  • Related