Lindsiria
Lindsiria

Reputation: 187

C++ Templates and Classes

I would like to sort different types of numbers in different files as a learning exercise. I've managed to get doubles and ints to sort correctly, but cannot get my own type (to sort a deck of cards) to format correctly. I've managed to get it narrowed down to my sort function in the Reader Class. I'm getting all sorts of errors. What exactly am I doing wrong? Or am I missing something? Thanks everyone, I hope I explained my problems clearly.

My file I created for Cards looks something like...

1 11 (meaning Jack of Diamonds)

2 8 (meaning 8 of hearts)

etc...

errors:

error: no match for 'operator<' in '((Reader)this)->Reader::array[compareIndex] < ((Reader)this)->Reader::array[smallestIndex]'

error: cannot convert 'Card' to 'int' in initialization

error: no match for 'operator=' in '((Reader*)this)->Reader::array[smallestIndex] = temp'

My Code

Lastly, Main

#include <iostream>
#include "Reader.h"
#include "Card.h"

using namespace std;


int main() {

    Reader<int> nums;
    Reader<double> dubs;
    Reader<Card> cards;
    int number;

    do {

        cout << "Enter 1 if your file contains integers\n"
           << "Enter 2 if your file contains doubles\n"
           << "Enter 3 if your file contains Cards:\n"
           << "Enter a number: ";
        cin >> number;

        if (number == 1){
            nums.open_IO();
            nums.read_array();
            nums.sort();
            nums.write_array();
            nums.close_IO();
        } else if (number == 2){
            dubs.open_IO();
            dubs.read_array();
            dubs.sort();
            dubs.write_array();
            dubs.close_IO();
        } else if (number == 3){
            cards.open_IO();
            cards.read_array();
            cards.sort();
            cards.write_array();
            cards.close_IO();
        }

    } while ((number != 1) && (number != 2) && (number != 3));


}

Reader.h

#ifndef READER_H
#define READER_H
#include <string>
#include <fstream>
using namespace std;

template <class rList>
class Reader {
 public:
    static const int SIZE = 50;

      Reader();

      bool open_IO();

      void close_IO();

      bool read_array();

      bool write_array();

      void sort();


 private:
      // Ask for the files
      void ask_filenames();

      rList array[SIZE];
      int readSize;

      // input file names and stream to get the data
      string readFileName;
      ifstream inFile;

      // output file names and stream to get the data
      string writeFileName;
      ofstream outFile;
      bool askedFileNames;
    };

#endif

#include "Reader.cpp"

My Reader.Cpp file

// File: Reader.cpp
#ifndef READER_CPP
#define READER_CPP
#include "Reader.h"
#include <iostream>
using namespace std;

/***************************************************************************
 * Contructors and modifying functions defined in "Reader.h"
***************************************************************************/


template <class rList>
Reader<rList>::Reader() {
  askedFileNames = false;
  readFileName = "";
  writeFileName = "";
  readSize = 0;
  rList empty;
  for( int i = 0; i<SIZE; i++ ) array[i]=empty;
}

template <class rList>
bool Reader<rList>::open_IO(){
    if(!askedFileNames) ask_filenames();

    inFile.open(readFileName.c_str()); //we can't pass a string, we need a char array
    if(!inFile.is_open()) {
        return false;
    }
    outFile.open(writeFileName.c_str());
    if(!outFile.is_open()) {
        inFile.close(); //inFile opened successfully so it needs to be closed now
        return false;
    }
    return true;
}

template <class rList>
void Reader<rList>::close_IO() {
    inFile.close();
    outFile.close();
}

template <class rList>
bool Reader<rList>::read_array() {
    if(inFile.is_open()) {
        inFile >> readSize;
        int index = 0;
        while(!inFile.eof() && index < readSize) {
            inFile >> array[index++]; //increments index after assigning value
        }
        readSize = index; //the input file could have had fewer numbers so set readSize
        return true;
    }
    return false;
}

template <class rList>
bool Reader<rList>::write_array() {
    if(outFile.is_open()) {
        outFile << readSize << endl;
       //don't forget the number indicating the element co
        int index = 0;
        while(index < readSize) {
            outFile << array[index++] << endl;
        }
        return true;
    }
    return false;
}

template <class rList>
void Reader<rList>::sort() {
    int startIndex = 0;
    int compareIndex;
    int smallestIndex;
    bool smallerFound;
    while(startIndex < readSize) {
        smallestIndex = startIndex;
        compareIndex = startIndex + 1;
        smallerFound = false;
        while(compareIndex < readSize) { //find the smallest value from the starting index
            if(array[compareIndex] < array[smallestIndex]) {
                smallestIndex = compareIndex;
                smallerFound = true;
            }
            compareIndex++;
        }
        if(smallerFound) { //only swap the values if a smaller value is found
            int temp = array[startIndex];
            array[startIndex] = array[smallestIndex];
            array[smallestIndex] = temp;
        }
        startIndex++;
    }
}

/*--------------------------------------------------------------
  This function asks the user for the filenames. This operation
  is placed in a separate function because it is called multiple
  times.
  --------------------------------------------------------------*/
template <class rList>
void Reader<rList>::ask_filenames() {
  cout << "Welcome. Please type in the name of the file to read the numbers.\n";
  cin >> readFileName;
  cout << "Thank you. Please type in the name of the file to write the numbers.\n";
  cin >> writeFileName;
  askedFileNames = true;
}

#endif

Card.h

#include <ostream>
#include <string>
#include "Reader.h"

using namespace std;

class Card{
 public:
    static const int SIZE = 50;
    enum SUIT {clubs, diams, hears, spads };
    enum RANK {ace=1, two, thr, fou, fiv, six, sev, eig, nin, ten, jac, que, kin};

    Card();

    Card(int newSuit, int newRank);

    void change(int newSuit, int newRank);

    friend ostream& operator << ( ostream& out, Card theCard );
    friend istream& operator >> ( istream& in, Card theCard );

 private:
    void change_rank(int newRank);

    void change_suit(int newSuit);

    string get_rank() const;

    string get_suit() const;

    SUIT suit;
    RANK rank;


};

Card.cpp

    #include <iostream>
#include <string>
#include "Card.h"

using namespace std;

Card::Card() { suit = spads; rank = ace; }

Card::Card(int newSuit, int newRank) {
    change(newSuit, newRank);
}

void Card::change( int newSuit, int newRank ) {
    change_suit( newSuit );
    change_rank( newRank );
}


ostream& operator << ( ostream& out, Card theCard ) {
  out << theCard.get_rank() << " of " << theCard.get_suit();
  return out;
}

istream& operator >> ( istream& in, Card theCard ) {
    int aSuit;
    int aRank;

    in >> aSuit >> aRank;
    return in;
}


// private member functions to set the private variables with their
// corresponding values: integer input -> enumerated type;
void Card::change_rank( int newRank ) {
    if( newRank == ace ) rank = ace;
    else if( newRank == two ) rank = two;
    else if( newRank == thr ) rank = thr;
    else if( newRank == fou ) rank = fou;
    else if( newRank == fiv ) rank = fiv;
    else if( newRank == six ) rank = six;
    else if( newRank == sev ) rank = sev;
    else if( newRank == eig ) rank = eig;
    else if( newRank == nin ) rank = nin;
    else if( newRank == ten ) rank = ten;
    else if( newRank == jac ) rank = jac;
    else if( newRank == que ) rank = que;
    else if( newRank == kin ) rank = kin;
}

void Card::change_suit( int newSuit ) {
    if( newSuit == clubs ) suit = clubs;
    else if( newSuit == spads ) suit = spads;
    else if( newSuit == diams ) suit = diams;
    else if( newSuit == hears ) suit = hears;
}

  // Private member functions to extract the information from the card
  // class.
string Card::get_rank() const {
    if( rank == ace ) return "ace";
    if( rank == two ) return "two";
    if( rank == thr ) return "three";
    if( rank == fou ) return "four";
    if( rank == fiv ) return "five";
    if( rank == six ) return "six";
    if( rank == sev ) return "seven";
    if( rank == eig ) return "eight";
    if( rank == nin ) return "nine";
    if( rank == ten ) return "ten";
    if( rank == jac ) return "jack";
    if( rank == que ) return "queen";
    if( rank == kin ) return "king";
    return "get_rank: error";
}

string Card::get_suit() const {
    if( suit == diams ) return "D";
    if( suit == hears ) return "H";
    if( suit == spads ) return "S";
    if( suit == clubs ) return "C";
    return "get_suit: error";
}

Upvotes: 0

Views: 258

Answers (2)

Emile Cormier
Emile Cormier

Reputation: 29229

If you used a std::vector<Card> instead of a C array, and you overloaded operator< for Card, you can get the standard library to shuffle and sort the deck for you.

vector<T> is a container that comes with standard library. It's a class template that can store elements of type T. T can be a number type, a std::string or even a struct or class.

Here's how you overload the less-than operator for Card:

bool Card::operator<(const Card& other)
{
    return true if this card is smaller than the other card;
    // You may want to order by rank, then by suit, or vice-versa.
}

With this operator defined, standard library containers and algorithms will now "know" how to order your Cards.

Here's how you can use a vector of Cards.

std::vector<Card> deck(52); // Initially sized to 52 elements
// Populate deck just like you would an array
deck[0] = Card(diams, ace);
etc...

// Sort the deck
std::sort(deck.begin(), deck.end());

// Shuffle the deck
std::random_shuffle(deck.begin(), deck.end());

// Get the 5th card in the deck
Card fifth = deck[4];

std::sort uses the quicksort algorithm, which is much faster than the bubble sort you've written (no offence :-). It's hard to write hand-made data structures and algorithms that can beat what the standard library provides. Compiler writers have spent years perfecting and tuning their standard library implementations.

When you use standard library containers, you have an arsenal of standard library algorithms at your disposal. This allows you to concentrate on solving problems, and not rewriting the same basic algorithms and data structures over and over.

Upvotes: 2

Drew Dormann
Drew Dormann

Reputation: 63945

You need to implement a comparison operator for Card

class Card{
 public:
  friend bool operator< ( const Card &left, const Card &right ) {
     ... 
  }

And you have a typo.

    if(smallerFound) { //only swap the values if a smaller value is found
        int temp = array[startIndex];
        array[startIndex] = array[smallestIndex];
        array[smallestIndex] = temp;
    }

should be

    if(smallerFound) { //only swap the values if a smaller value is found
        rList temp = array[startIndex];
        array[startIndex] = array[smallestIndex];
        array[smallestIndex] = temp;
    }

or more simply

    if(smallerFound) { //only swap the values if a smaller value is found
        std::swap( array[startIndex], array[smallestIndex] );
    }

Upvotes: 1

Related Questions