I am working on a hangman program and I want to output a status report to the user after each guess. As I am using classes, I need to use a 'friend' keyword. I'm ok with the concept of classes and friends however I am struggling to implement it properly in my program.
The main issue is that the numbers of vowels is not being counted and the number of letters the user can try is not updating. The full alphabet is being displayed each time.
The code of my program is substantial and I'm not going to post all of it here. The issues I have are with the functions remainingLetters(); and vowelCount(). I have attached the relevant code snippets. This code will obviously not compile. I'm just hoping that someone might see an obvious mistake that I've missed.
**Hangman.h:**
{
public:
...
int vowelCount()const; //to be implemented
char* remainingLetters()const;//to be implemented
friend ostream& operator<< (ostream &out, Hangman &game);
private:
...
int vowel_count;
char secretWord[MAXWORDLENGTH];
char* remaining_letters;
}
**hangman.cpp:**
{
...
int Hangman::vowelCount()const
{
int vowel_count = 0;
int i;
secretWord[i];
for(i=0; i<strlen(secretWord); i++)
{
if(secretWord[i]=='a'||secretWord[i]=='e'||secretWord[i]=='i'||secretWord[i]=='o'||secretWord[i]=='u')
{
vowel_count++;
}
}
return vowel_count;
}
char* Hangman::remainingLetters()const
{
char alphabet[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
char *remaining_letters=new char[ALPHABETSIZE];
for(int i=0; i<strlen(secretWord); i++)
{
for(int j=0; j<ALPHABETSIZE; j++)
{
if(secretWord[i]!=alphabet[j])
{
remaining_letters[j]=alphabet[j];
}
}
}
return remaining_letters;
}
ostream& operator<< (ostream &out, Hangman &game)
{
out << "Number of guesses remaining is: " << game.numGuessesAllowed-game.numWrongGuesses << endl
<< "Number of incorrect guesses so far is: "<<game.numWrongGuesses <<endl
<< "Letters remaining are: "<<game.remaining_letters <<endl
<< "Hint: The secret word contains "<<game.vowel_count <<" vowels"<<endl;
return out;
}
}
**main.cpp**
{
...
cout << game;
...
return 0;
}
You need to call the functions vowelCount() and remainingLetters() for the corresponding variables to be calculated (so that you can output them).
Also, char *remaining_letters=new char[ALPHABETSIZE]; will allocate the memory for the letters and initialize each position with 0 -- which also happens to be the string terminating value. So if the first position (letter 'a') is not set, out<<game.remaining_letters will not output anything
Moreover, the function vowelCount() defines the local variable vowel_count that hides the one in the class (member variable), so the member variable will not get updated (uless you explicitly assign the return value of vowelCount() to that member variable) I suggest you delete the local variable declaration from the vowelCount() function and it will use the member variable, which is what you need
The previous paragraph also applies to the member variable remaining_letters
One more thing: the memory allocated with new is not deallocated automatically, and once you return from the function call, the allocated memory is lost (as in there is no way to access it again, but it still uses memory), unless you assign it when the function returns. You will need to pari each new[] call with its corresponding delete[] call. Better yet: allocate the memory in the constructor of the class and delete it in the destructor for the member variable
Note: yet another bug is in this logic:
for(int i=0; i<strlen(secretWord); i++)
{
for(int j=0; j<ALPHABETSIZE; j++)
{
if(secretWord[i]!=alphabet[j])
{
remaining_letters[j]=alphabet[j];
}
}
}
you cycle through the alphabet and every time the current letter in secret word is not matvching, you assign to remaining_letters. This will assignment will happen every time as there will be a letter in the alphabet that does not match the curent secretWord letter.
To fix this, you will need do something like this (note the inversion of the loops):
int secret_len = strlen(secretWord); // cache length, so we don't recalculate it
// every time: secretWord does not change
for(int j=0; j<ALPHABETSIZE; j++)
{
bool found = false; // assume the current ABC letter is not in secretWord
for(int i=0; i<secret_len; i++)
{
if(secretWord[i]!=alphabet[j])
{
found = true; // but it was
}
}
if (!found) // if it wasn't
{
remaining_letters[j]=alphabet[j];
}
}
Your main problem is that you do not keep the vowels count in the class member vowel_count. You store it in a local variable vowel_count and return it but the class member is never updated.
Related
I have class that contains some functions with an array. One of them called pick, this function does not has an array but i want to display array element from other function, once i write code to display array element i can not get the element, although the code work and if i add normal text it will display but can not display array elements, i will attach the code below:
Note i did not attache full code only important parts
#include <iostream>
#include<stdlib.h>
#include <string>
using namespace std;
// global variable
string plantType;
int temperatures ;
string water;
string sunExposure;
bool pet;
string plant1,plant2,plant3;
class inside{
public:
void low(){
string plant1 [3]={"Snake plant","Spider plant","Aloe Vera plant"};
for(int i =0; i<3; i++){
cout << "* "<< plant1[i]<<endl;
}
}
void medium(){
string plant2 [5]={"Pothos plant","Dracaena plant","ZZ plant","Rubber plant","Philodendron Green plant"};
for(int i =0; i<5; i++){
cout << "* "<< plant2[i]<<endl;
}
}
void high(){
string plant3 [2]={"Bird’s Nest Fern plant","Peace Lily plant"};
for(int i =0; i<2; i++){
cout << "* "<< plant3[i]<<endl;
}
}
void pick(){
cout <<"Best choice for you is: ";
if (temperatures >= 13 && temperatures <=29 ){
if(water=="low"){
if(sunExposure=="fully"){
cout<<"test"<<endl;
if(pet==true){
cout<<plant1[1]<<endl; //this line cannot be executed
}
}
}}}
int main(){
cout <<"Where do you want to grow the plant (inside), or (outside)"<<endl;
cin>>grow;
if (grow == "inside"){
//inside home
cout<<endl<<"inside"<<endl;
cout<<"Enter the Temperature in (Celsius scale 13-29)"<<endl;
cin>>temperatures;
cout<<"Enter water level (low - medium - high)"<<endl;
cin>>water;
cout<<"Enter Sun Exposure (fully - partly - shady)"<<endl;
cin>>sunExposure;
cout<<"Do you have pet (true or false)? "<<endl;
cin>>pet;
inside inside;
inside.pick();
}
}
The string variables plant1[3], plant2[3] & plant3[3] are only visible to low(), medium(), high() but they're not for each other. For example, you're something trying to access a locally declared variable of foo() function in bar() function, which is impossible.
Secondly, you're trying to access plant1[3] data member in pick() but compiler doesn't know where you've actually defined those three plants' name outside of that function, just it knows plant1 variable with no arrays which is declared in the private section of the class. Hence, there's a good chance your code will get fail.
Rather you could just declare the same thing in this way:
class inside {
std::string plants1[3] = {"...", ...}; // these variables are
std::string plants2[3] = {"...", ...}; // only visible inside the
std::string plants3[3] = {"...", ...}; // class member functions
.
.
}
And after that, you'll get the line executed successfully.
I'm having a bit of trouble figuring out exactly what I am doing wrong here, and haven't found any posts with the same issue. I am using a dynamic array of strings to hold a binary tree with the root at [0], first row of children, left to right, at [1] and [2], etc. While I haven't debugged that output format yet, I am much more concerned as to why that specific line is crashing my program.
I thought it was a pointer de-referencing issue, but outStream << &contestList[i] prints addresses as I'd expect, and outStream << *contestList[i] throws errors as I'd expect them to.
//3 lines are from other functions/files
typedef string elementType;
typedef elementType* elementTypePtr;
elementTypePtr contestList = new elementType[arraySize];
void BinTreeTourneyArray::printDownward(ostream &outStream)
{
int row = 1;
for (int i = 0; i < getArraySize(); i++)
{
outStream << contestList[i]; //this is crashing the program
if (isPowerOfTwo(i))
{
outStream << endl;
row++;
}
else
{
outStream << ":";
}
}
}
arraySize is a private member arraySize = ((2 * contestants) - 1) where contestants is the number of contestants in my tournament. Each round or "row" in the tree is synonymous with a tournament bracket. If there are n contestants, then there are 2n-1 nodes needed in the tree. The issue wouldn't be with this function.
getArraySize() { return arraySize; }
Turns out elementTypePtr contestList = new elementType[arraySize]; was the issue. contestList was a private member of the class, then I threw this line in a function declaring a local variable of the same name that disappears after the function ends. No biggie, except for the fact that I needed it in the print function...Oops.
This is the motivation behind the code. There is a boy named Bob and its his birthday today. He invites 50 friends over but not all of his friends want to buy him gifts. Bob is presented with 50 presents, though some of them are empty. His good friends tell him to close every 2nd box. For every third box, he is supposed to change every closed to open and every open to closed. He continues to do this for every n-th box where n is less than 50. The open boxes in the end will have the presents.
This is supposed to assist me in figuring out a problem for my math class, but I am not aware of all the complicated aspects of C++ programming. I want my string getValue(vector &arr) to return an array/vector. This code doesn't compile but it shows what I'm trying to do.
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
string getValue(vector<string> &arr);
int main()
{
vector<string> myArr(2);
vector<string> newArr(2);
for(int i=2; i <= 50; i++)
{
if(i%2==0)
{
myArr.push_back("close");
}
else
{
myArr.push_back("open");
}
}
newArr = getValue(myArr);
for(int i=2; i <=50; i++)
{
cout << i << " " << newArr[i] << endl;
}
}
string getValue(vector<string> &arr)
{
for(int i=2; i <=50; i++)
{
if(arr[i]=="close")
{
arr[i]="open";
}
else if(arr[i]=="open")
{
arr[i]="close";
}
}
return arr;
}
You can't make your string getValue(vector<string> &arr) return an array/vector. It can only return a string. If you want a function to return an array/vector, then you have to say so in the function signature.
You're passing the vector into getValue() by reference, which means changes you make to it in that function will affect the original (in other words, you're not operating on a copy of the vector - you're actually operating on the vector).
So you don't need to return anything from getValue() - just make it void and it should do what you want.
string getValue(vector &arr) - the return type is string, not vector. You need to change its return type or set it to none.
PS:
newArr = getValue(myArr);
it's behind the SCOPE and it's wrongly positioned...
damn, third PS, wrong code rules are assigned
For the syntax part :-
The return type of the function is a string. Change it to vector for
your function to work properly.
You can simply declare the vectors globally. This will eliminate the
need to pass it to the function as well as return it.
For the logic part :-
Your question says that Bob toggles every third box but in your program Bob is changing every box to open if it is closed and every box to close if it is open. If what you wrote in the question is correct your code should be like this.
#include <iostream>
#include <vector>
using namespace std;
void getValue();
vector<string> myArr(2);
int main()
{
for(int i=2; i <= 50; i++)
{
if(i%2==0)
{
myArr.push_back("close");
}
else
{
myArr.push_back("open");
}
}
getValue();
for(int i=2; i <=50; i++)
{
cout << i << " " << myArr[i] << endl;
}
}
void getValue()
{
for(int i=3; i <=50; i+=3)
{
if(myArr[i]=="close")
{
myArr[i]="open";
}
else if(myArr[i]=="open")
{
myArr[i]="close";
}
}
}
Solved
Solution: provide the copy constructor and follow the rule of three.
Original problem
I'm implementing a Class that is a Matrix of intervalls.
My Intervall Class looks like this (shortned):
class Intervall
{
friend std::ostream& operator<<(std::ostream& output, const Intervall& i);
public:
Intervall();
Intervall(double n);
private:
double min_value;
double max_value;
};
With this implementation:
Intervall::Intervall(){
min_value = 0;
max_value = 0;
}
Intervall::Intervall(double n){
min_value = n;
max_value = n;
}
ostream& operator<<(ostream& output, const Intervall& i){
output << "[" << i.min_value << ", " << i.max_value <<"]";
return output;
}
My Matrix class looks like this:
class IntervallRMatrix
{
friend std::ostream& operator<<(std::ostream& output, const IntervallRMatrix& irm);
public:
IntervallRMatrix();
~IntervallRMatrix();
void setIdentity();
IntervallRMatrix clone();
private:
void initialize();
Intervall* data;
Intervall** cells;
};
with the following implementation:
IntervallRMatrix::IntervallRMatrix(){
initialize();
}
IntervallRMatrix::~IntervallRMatrix(){
delete cells;
delete data;
}
ostream& operator<<(ostream& output, const IntervallRMatrix& irm){
output << "3 x 3" << endl;
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
output << irm.cells[i][j];
}
output << endl;
}
return output;
}
void IntervallRMatrix::setIdentity(){
cells[0][0] = Intervall(1);
cells[1][1] = Intervall(1);
cells[2][2] = Intervall(1);
}
IntervallRMatrix IntervallRMatrix::clone(){
IntervallRMatrix result;
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
result.cells[i][j] = cells[i][j];
}
}
return result;
}
void IntervallRMatrix::initialize(){
data = new Intervall[9];
for(int i=0; i<9; i++){
data[i] = Intervall();
}
cells = new Intervall*[3];
for(int i=0; i<3; i++){
cells[i] = &data[3*i];
}
}
This all works fine until I use the clone function. This code produces a memory error:
int main()
{
IntervallRMatrix m1;
m1.setIdentity();
cout << "m1: " << m1 << endl;
IntervallRMatrix m2 = m1.clone();
cout << "m2: " << m2 << endl;
return 0;
}
The first cout works as intended.
The second cout doesn't. The error comes right when the program tries to read m2.cells[0][0].
This error does not occur if I don't delete cells and data in the destructor.
I declare cells and data in the way I do because I want to overload the square brackets so that I can later write Intervall = Matrix[i][j].
I also want to overload the arithmetic operators so that I can write Matrix m3 = m2*m1.
In fact, I already did that, that's where the error first occured.
Now, finally my Question:
How can I implement a function that returns an IntervallRMatrix that won't cause a memory error while still being able to free the allocated memory in the destructor?
The best solution is to avoid manual memory management. Use an appropriate container (e.g. std::vector), or a smart pointer.
If you must do manual management, then ensure you follow the Rule of Three.
The issue here is that the clone method returns a copy of the result object you just created on the stack and initialised.
As you did not implment any copy constructor (from what we can see) a default one is provided by the compiler. This default implementation will basically copy the member variable from one instance to another (in this case, the pointers to cells and data). The content will not be duplicated at all.
Once you get out of the clone function scope, the result object gets deleted (as pre desctructor code) and you end up with a copy of result with the cells and data pointers still pointing to the just free memory. This leads to an undefined behavior (often read as "it will crash")
Follow the Rule of Three as Oli Charlesworth put in his answer.
I'm again doing a task for school and I'm implementing it slowly, I don't know why my park_car function is not working, I just wanted to make a test and the program crashes ... here is my code.
PS: I can't change the ***p2parkboxes because it is given in the starter file like most other variables. I just want to see the first element of Floor 0 as : HH-AB 1234. Your help is most appreciated.
PS2: I can't use the std::string as well it isn't allowed for the task.
#include <iostream>
#include <cstring>
using namespace std;
#define EMPTY "----------"
class Parkbox{
char *license_plate; // car's license plate
public:
Parkbox(char *s = EMPTY); // CTOR
~Parkbox(); // DTOR
char *get_plate(){return license_plate;}
};
class ParkingGarage{
Parkbox ***p2parkboxes;
//int dimensions_of_parkhouse[3]; // better with rows,columns,floors
int rows,columns,floors; // dimensions of park house
int total_num_of_cars_currently_parked;
int next_free_parking_position[3];
// PRIVATE MEMBER FUNCTION
void find_next_free_parking_position();
public:
ParkingGarage(int row, int col, int flr);// CTOR,[rows][columns][floors]
~ParkingGarage(); // DTOR
bool park_car(char*); // park car with license plate
bool fetch_car(char*); // fetch car with license plate
void show(); // show content of garage floor
// by floor
};
Parkbox::Parkbox(char *s ) { // CTOR
license_plate = new char[strlen(s)+1];
strcpy(license_plate, s);
//cout << "ParkBox CTOR" << endl;
}
Parkbox::~Parkbox() { // DTOR
delete [] license_plate;
//cout << "ParkBox DTOR" << endl;
}
ParkingGarage::ParkingGarage(int row, int col, int flr){
rows = row; columns = col; floors = flr;
p2parkboxes = new Parkbox**[row];
for (int i = 0; i < row; ++i) {
p2parkboxes[i] = new Parkbox*[col];
for (int j = 0; j < col; ++j)
p2parkboxes[i][j] = new Parkbox[flr];
}
}
ParkingGarage::~ParkingGarage(){
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j)
delete [] p2parkboxes[i][j];
delete [] p2parkboxes[i];
}
delete [] p2parkboxes;
}
void ParkingGarage::show(){
int i,j,k;
for (i = 0 ; i < floors; i++){
cout << "Floor" << i << endl;
for (j=0;j<rows;j++){
for (k=0;k<columns;k++){
cout << p2parkboxes[j][k][i].get_plate() << " ";
}
cout << endl;
}
}
}
bool ParkingGarage::park_car(char*s){
p2parkboxes[0][0][0] = Parkbox(s); //test
//p2parkboxes[0][0][0] = s; //test
return true;
}
int main(void) {
// a parking garage with 2 rows, 3 columns and 4 floors
ParkingGarage pg1(2, 3, 4);
pg1.park_car("HH-AB 1234");
/*pg1.park_car("HH-CD 5678");
pg1.park_car("HH-EF 1010");
pg1.park_car("HH-GH 1235");
pg1.park_car("HH-IJ 5676");
pg1.park_car("HH-LM 1017");
pg1.park_car("HH-MN 1111"); */
pg1.show();
/*pg1.fetch_car("HH-CD 5678");
pg1.show();
pg1.fetch_car("HH-IJ 5676");
pg1.show();
pg1.park_car("HH-SK 1087");
pg1.show();
pg1.park_car("SE-AB 1000");
pg1.show();
pg1.park_car("PI-XY 9999");
pg1.show(); */
return 0;
}
You did not declare the copy constructor for the Parkbox class. So, the line
p2parboxes[0][0][0] = Parkbox(s)
creates something (instance of Parkbox with a char* pointer) on the stack (and deletes it almost immediately). To correct this you might define the
Parkbox& operator = Parkbox(const Parkbox& other)
{
license_plate = new char[strlen(other.get_plate())+1];
strcpy(license_plate, other.get_plate());
return *this;
}
Let's see the workflow for the
p2parboxes[0][0][0] = Parkbox(s)
line.
First, the constructor is called and an instance of Parkbox is created on stack (we will call this tmp_Parkbox).
Inside this constructor the license_plate is allocated and let's say it points to 0xDEADBEEF location.
The copying happens (this is obvious because this is the thing that is written in code) and the p2parboxes[0][0][0] now contains the exact copy of tmp_Parkbox.
The scope for tmp_Parkbox now ends and the destructor for tmp_Parkbox is called, where the tmp_Parkbox.license_plate (0xDEADBEEF ptr) is deallocated.
p2parboxes[0][0][0] still contains a "valid" instance of Parkbox and the p2parboxes[0][0][0].license_plate is still 0xDEADBEEF which leads to the undefined behaviour, if any allocation occurs before you call the
cout << p2parboxes[0][0][0].license_plate;
Bottom line: there is nothing wrong with the line itself, the problem is hidden within the implementation details of the '=' operator.
At this point it is really better for you to use the std::string for strings and not the razor-sharp, tricky and explicit C-style direct memory management mixed with the implicit C++ copy/construction semantics. The code would also be better if you use the std::vector for dynamic arrays.
The problem here is that you do not have deep copy assignment semantics. When you assign a temporary Parkbox to the Parkbox in the parking garage, the compiler generated assignment operator makes a shallow copy of the pointer license_plate, leaving both Parkboxes pointing at the same memory location. Then the temporary Parkbox goes out of scope and deletes license_plate. Since the other Parkbox is pointing at the same spot its license_plate gets deleted, too.
There are a couple solutions. One way to solve the problem is to define an assignment operator and a copy constructor that provide proper semantics, i.e. that perform deep copies of the license plate string. The better option, and the one that makes better use of C++, is to use std::strings instead of manually allocated C-strings. I strongly suggest the second approach, though working through the first might be instructive.
From the OP:
I solved the Problem with :
void Parkbox::change_plate(char *s){
delete [] license_plate;
license_plate = new char[strlen(s)+1];
strcpy(license_plate, s);
}