Reputation: 187
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
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 Card
s.
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
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