C++ inheritance with overloading not compiling?

2019-07-18 13:24发布

问题:

I am making a Poker game in C++, and I am just trying to get started. I need the ability to compare "Hands", to see which one is greater, equal, or lesser. So, I have a Hand class now, and I made two other sub-classes that are called Straight and ThreeOfKind (I will add the rest later. The Hand class has a method called compareTo(Hand* otherHand), it then checks the hand ranking to see which one is better. Also, with the Straights and Three of a Kinds, you can compare them together when they are of the same rank. Like Straights with Straights and Three of a Kinds with Three of a Kinds.

I wrote some initial code today, and my problem is, when I try to call "Hand's" compareTo(Hand* otherHand) method and pass in a Hand, Straight, or Three of a Kind, the compiler complains as it is trying to force me to use the Straight's compareTo(Straight* otherStraight) method. So, I should have overloading, but it's not working.

So in the Straight class after inheritance is done, we should have these two methods:

int Hand::compareTo(Hand* otherHand);
int Straight::compareTo(Straight* otherStraight);

// But, if you do this, it works:
Straight myStraight1 = new Straight(7);
Straight myStraight2 = new Straight(5);
myStraight1.compareTo(myStraight2);
// This is valid...

// If you do this, then the compiler complains!
Straight myStraight3 = new Straight(10);
ThreeOfKind myTrips4 = new ThreeOfKind(3);
myStraight3.compareTo(myTrips4);
// This above line complains that you cannot convert a ThreeOfKind to a Straight
// Even though I am trying to use Hand's compareTo(Hand* otherHand) method and
// cast a Three of a Kind to a Hand object,
// it fails with the overloading!

Here is all the source code...

//////////////////////////
// C++ main header file //
//////////////////////////


#pragma once


class Hand {

private:
    int ranking;

public:
    Hand(int aRanking);
    Hand();

    int getRanking();

    int compareTo(Hand* otherHand);
};

class Straight : public Hand {

private:
    int highCard;

public:

    Straight(int aHighCard);
    Straight();

    int getHighCard();

    int compareTo(Straight* otherStraight);
};

class ThreeOfKind : public Hand {

private:
    int tripsValue;

public:

    ThreeOfKind(int aTripsValue);
    ThreeOfKind();

    int getTripsValue();

    int compareTo(ThreeOfKind* otherThreeOfKind);
};


///////////////////////////
// C++ main .cpp file... //
///////////////////////////
#include <iostream>
#include "PokerTest1.h"
using namespace std;

Hand::Hand(int aRanking) {

    this->ranking = aRanking;
}

Hand::Hand() {

    this->ranking = 0;
}

int Hand::getRanking() {
    return this->ranking;
}

int Hand::compareTo(Hand* otherHand) {

    cout << "COMPARING HANDS..." << endl;

    if (this->getRanking() < otherHand->getRanking()) {

        cout << "HANDS RETURNING -1..." << endl;

        return -1;
    }
    else if (this->getRanking() > otherHand->getRanking()) {

        cout << "HANDS RETURNING 1..." << endl;

        return 1;
    }

    cout << "HAND RANKINGS ARE EQUAL..." << endl;

    if (this->getRanking() == 4 && otherHand->getRanking() == 4) {

        cout << "HANDS ARE BOTH STRAIGHTS..." << endl;

        Straight* myStraight1 = (Straight*)this;
        Straight* myStraight2 = (Straight*)otherHand;

        cout << "COMPARING BOTH STRAIGHTS..." << endl;

        return myStraight1->compareTo(myStraight2);
    }
    else if (this->getRanking() == 3 && otherHand->getRanking() == 3) {

        cout << "HANDS ARE BOTH THREE OF A KINDS..." << endl;

        ThreeOfKind* myTrips1 = (ThreeOfKind*)this;
        ThreeOfKind* myTrips2 = (ThreeOfKind*)otherHand;

        cout << "COMPARING BOTH TRIPS..." << endl;

        return myTrips1->compareTo(myTrips2);
    }

    return 0;
}

Straight::Straight(int aHighCard) : Hand(4) {
    this->highCard = aHighCard;
}

Straight::Straight() : Hand(4) {
    this->highCard = 0;
}

int Straight::getHighCard() {
    return this->highCard;
}


int Straight::compareTo(Straight* otherStraight) {

    cout << "INSIDE STRAIGHT COMPARE TO..." << endl;

    if (this->highCard < otherStraight->highCard) {

        cout << "STRAIGHT COMPARE RETURNING -1..." << endl;

        return -1;
}
    else if (this->highCard > otherStraight->highCard) {

        cout << "STRAIGHT COMPARE RETURNING 1..." << endl;

        return 1;
    }

    cout << "STRAIGHT COMPARE RETURNING 0..." << endl;

    return 0;
}


ThreeOfKind::ThreeOfKind(int aTripsValue) : Hand(3) {
    this->tripsValue = aTripsValue;
}

ThreeOfKind::ThreeOfKind() : Hand(3) {
    this->tripsValue = 0;
}

int ThreeOfKind::getTripsValue() {
    return this->tripsValue;
}

int ThreeOfKind::compareTo(ThreeOfKind* otherThreeOfKind) {

    cout << "INSIDE STRAIGHT COMPARE TO..." << endl;

    if (this->tripsValue < otherThreeOfKind->tripsValue) {

        cout << "TRIPS COMPARE RETURNING -1..." << endl;

        return -1;
    }
    else if (this->tripsValue > otherThreeOfKind->tripsValue) {

        cout << "TRIPS COMPARE RETURNING 1..." << endl;

        return 1;
    }

    cout << "TRIPS COMPARE RETURNIN 0..." << endl;

    return 0;
}

int main()
{

    // Test the classes...
    // with Straight compared to a Three of a Kind.
    // Should try to invoke Hand::compareTo(Hand* otherHand) { ... };
    // But, instead, it try to invoke Straight::compareTo(Straight* otherStraight) { ... };

    // If you put both these methods in the Straight class (rather than using inheritence, it works)
    // If  you delete Straight::compareTo(Straight* otherStraight) { ... }, the line below compiles

    // It is just strange why it won't compile...
    Straight* myStraightA = new Straight(9); // Straight of 5, 6, 7, 8, 9
    ThreeOfKind* myTripsB = new ThreeOfKind(2); // Three of a Kind of 2, 2, 2

    cout << "Compare results..." << endl;
    cout << myStraightA->compareTo(myTripsB) << endl; // Compiler error...

    return 0;
}

Also, here is a list of the hand rankings:

0 → high card
1 → pair
2 → two pair
3 → three of a kind
4 → straight
5 → flush
6 → full house
7 → quads
8 → straight flush
9 → royal flush

Basically I have a field in the Hand class that stores these rankings as integers. Just so you know.

Lastly, this is the compiler error message:

error C2664: 'int Straight::compareTo(Straight )': cannot convert argument 1 from 'ThreeOfKind' to 'Straight*'

回答1:

You are trying to overload across classes.

The compiler looks for a compareTo method, finds it in Straight, and doesn't look at Hand. If you add an appropriate using statement, you can tell it to look at Hand's compareTo as well to accomplish your overloading.

class Straight : public Hand {
private:
    int highCard;

public:
    using Hand::compareTo; // <<< HERE

    Straight(int aHighCard);
    Straight();

    int getHighCard();

    int compareTo(Straight* otherStraight);
};

Instead of doing this, I'd recommend you use getRanking() for comparison between hands of different hand types, and define getTieBreaker() overridden by subclasses to handle cases of the same type of hand.

class Hand {
public:
    int getRanking();
    // virtual causes subclass' version to be called if done from reference or pointer.
    virtual int getTieBreaker();  
};

This simplifies how Hand compares:

int Hand::compareTo(Hand* otherHand) {
    if (this->getRanking() < otherHand->getRanking()) {
        return -1;
    }
    else if (this->getRanking() > otherHand->getRanking()) {
        return 1;
    }

    if (this->getTieBreaker() < otherHand->getTieBreaker()) {
        return -1;
    }
    else if (this->getTieBreaker() > otherHand->getTieBreaker()) {
        return 1;
    }
    return 0;
}

And lets you define it in Straight:

class Straight : public Hand {
//...
public:
    int getHighCard();
    int getTieBreaker();
};

int Straight::getTieBreaker() {
    return this->highCard;
}