Hello! I have 2 classes, Tree and Forest. Forest uses Tree's functions in order to build a forest and print it. I've been trying to debug my code for several hours now and I think I need a pair of fresh eyes to help me figure out what's wrong.
An example of the error I keep getting is "request for member 'plant_tree' in 'forest', which is of non-class type 'Forest()'". I have a bunch of this same error in main.cpp and forest.cpp, so I think the error has to do with class scope.
Here is my code so far (header files were provided to me by my professor):
#ifndef _TREEH
#define _TREEH
class Tree {
public:
Tree();
Tree(int x, int y); // Constructor for Tree, taking in the x and y coordinates!
//
int getX(); // Returns the x coordinate
int getY(); // Returns the y coordinate
char getSymbol(); // Returns the symbol, * or -
void setX(int x_coordinate); // Sets X
void setY(int y_coordinate); // Sets Y
void setSymbol(char soil_or_tree); // Sets the symbol, * or -
private:
int x; // Declare integer, x (value ranges [1,10])
int y; // Declare integer, y (value ranges [1,4])
char symbol; // Declare character, symbol
};
#endif
#include "tree.h"
#include "forest.h"
#include <iostream>
#include <vector>
usingnamespace std;
int main(){
Forest forest(); // Creating an object for the class, Forest. Default forest is now initialized.
forest.plant_tree(1,1); // Planting a tree at 1,1
forest.print(); // Printing the forest with the tree at 1,1
return 0;
}
EDIT: Looking over my code, I realized I didn't use my private vector, trees, of Tree objects... I'm really not sure how I should be using this vector. I'm guessing it should replace my method of adding the trees to my own vector, the_forest.
How would I use this vector (trees) if it's private and I can't use any other functions besides the ones given to me in the header files?
Forest can access Forest::trees.
plant_tree() should create a Tree with x,y, make it have '*', and append to trees.
cut_tree() should check whether trees contains a tree with x,y. Then ...
Line 11 of main.cpp:
Do not use parentheses for default construction. The compiler will interpret this: Forest forest();
as a function prototype of a function named "forest" that takes no parameters and returns a Forest object. That is why the error tells you that the function type (Forest()) has no member called "plant_tree()".
Lines 9 and 14 of forest.cpp:
Why is there a global variable, and why is there a local variable in the constructor with the same name as the global one?
How would I use this vector (trees) if it's private and I can't use any other functions since the header files were given to me?
Remember that objects can access their own private variables. For example:
1 2 3 4 5 6 7
class Forest{
public:
//Forget the other methods, let's focus on this constructor I'll add:
Forest(std::size_t num_to_gen);
private:
std::vector<Tree> trees;
};
Then, when defining Forest:Forest(std::size_t):
1 2 3 4 5 6 7 8
Forest::Forest(std::size_t num_to_gen)
: trees(num_to_gen, Tree()) //This is called an initializer list. It is like typing std::vector<Tree> trees (size, Tree());
{}
//An alternative way to write this would be:
Forest::Forest(std::size_t num_to_gen){
trees.resize(num_to_gen, Tree());
}
Regarding line 11 of main.cpp, thank you! I did not catch that. It has been fixed and it got rid of those errors :D
I declared the global variable the_forest outside the constructor to be a prototype for the variable so that I could use it outside of the constructor in the print() function. I mean, I know I should be putting all the prototypes in the header file, but I'm not allowed to change it so I just sort of cheated by declaring it in my source file. Is it improper to do so?
Ah, I see.
I'm still having a lot of trouble trying to figure out how I should be using the vector 'trees.'
And, was my method of using the_forest[y][x] = cutting.getSymbol() viable? Should I be keeping something similar using the vector trees, or should I try to change it completely.
I declared the global variable the_forest outside the constructor to be a prototype for the variable so that I could use it outside of the constructor in the print() function.
the_forest global and the_forest local are two completely separate variables that happen to share the same name. The compiler should tell you that the local name "shadows" the global one.
This may seem weird, but treat the variable Forest::trees not as a collection of trees but rather a collection of soil and trees. Your Tree class shows it allows the option for the symbol to either be soil or a tree.
And, as keskiverto has stated, all you'd have to do is change its symbol accordingly. There is no need for the the_forest variable.
You should have everything you need within the class.
I got rid if the_forest, but I'm now getting a huge chain of errors, repeating things along the lines of "ostream note: template<class _Traits> std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, signed char)"
the_forest global and the_forest local are two completely separate variables that happen to share the same name. The compiler should tell you that the local name "shadows" the global one.
I did not know that happens. Thanks for explaining; it cleared up a lot!
This may seem weird, but treat the variable Forest::trees not as a collection of trees but rather a collection of soil and trees.
I was having trouble seeing how to use a one-dimensional vector, but this helped a lot! I can see it now.
#ifndef _TREEH
#define _TREEH
class Tree{
public:
Tree();
Tree(int x, int y); // Constructor for Tree, taking in the x and y coordinates!
//
int getX(); // Returns the x coordinate
int getY(); // Returns the y coordinate
char getSymbol(); // Returns the symbol, * or -
void setX(int x_coordinate); // Sets X
void setY(int y_coordinate); // Sets Y
void setSymbol(char soil_or_tree); // Sets the symbol, * or -
private:
int x; // Declare integer, x (value ranges [1,10])
int y; // Declare integer, y (value ranges [1,4])
char symbol; // Declare character, symbol
};
#endif
#include "tree.h"
#include <iostream>
usingnamespace std;
void Tree::setX(int x_coordinate){
x = x_coordinate;
}
void Tree::setY(int y_coordinate){
y = y_coordinate;
}
void Tree::setSymbol(char soil_or_tree){
symbol = soil_or_tree;
}
int Tree::getX(){
return x;
}
int Tree::getY(){
return y;
}
int Tree::getSymbol(){
return symbol;
}
Tree::Tree(){}
Tree::Tree(int x, int y){
Tree::setX(int x);
Tree::setY(int y);
Tree::getX();
Tree::getY();
}
main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include "tree.h"
#include "forest.h"
#include <iostream>
#include <vector>
usingnamespace std;
int main(){
Forest forest; // Creating an object for the class, Forest. Default forest is now initialized.
forest.plant_tree(1,1); // Planting a tree at 1,1
forest.print(); // Printing the forest with the tree at 1,1
return 0;
}
I'm really confused as to why I'm getting errors related to the << operator.
Line 52 of forest.cpp:
[code cout << trees[column] << " ";[/code]
Remember that trees is a container of Tree objects. You should probably call getSymbol().
Line 11 of forest.cpp: vector<Tree> Forest::trees(40,'-');
I am not exactly sure what this would result in, but it looks like an attempt to define a separate variable.
Keep this in mind about constructors:
-All data member variables are initialized before the function body, so:
1 2 3 4
Forest::Forest()
{ // <-- The variable Forest::trees has already been created and initialized
trees.resize(4, Tree()); // <-- You are free to use the member variable
}
If you want to change what the variable is initialized with, you use something called the initializer list:
1 2 3
Forest::Forest()
: trees(4, Tree()) //List starts with colon; pass parameters like you would when declaring and initializing
{}
You can look at the available Tree constructors to tell what to do. You have two choices:
-Tree::Tree() (Default construction)
-Tree::Tree(int, int) (Construct with coordinates)
I don't really know which one you are supposed to use, but either way, you'll probably have to do some quick loops to set up the coordinates. (Unless all the trees should start at the same coordinate.)
A note about your Tree constructors:
You should initialize your variables. For example, in both constructors, you never initialize the symbol, so there is no default symbol and the user is forced to manually set one even if he/she wants the default.
If you are not required to set up coordinates when constructing, you don't have to do it. I was pointing out that in your Forest constructor, you create 40 Tree objects who have unknown coordinates.
Line 11 of forest.cpp:
vector<Tree> Forest::trees(40,'-');
I am not exactly sure what this would result in, but it looks like an attempt to define a separate variable.
Keep this in mind about constructors:
-All data member variables are initialized before the function body, so:
1 2 3 4
Forest::Forest()
{ // <-- The variable Forest::trees has already been created and initialized
trees.resize(4, Tree()); // <-- You are free to use the member variable
}
If you want to change what the variable is initialized with, you use something called the initializer list:
1 2 3
Forest::Forest()
: trees(4, Tree()) //List starts with colon; pass parameters like you would when declaring and initializing
{}
1 2 3 4 5
Tree::Tree(){
void Tree::setSymbol('-'); // Default symbol
}
Oh, I see!
General question about using classes since I'm not completely comfortable with using them, yet: When you have something like Tree::Tree(){} or Tree::someFunction(){}, and it has a function or variable from the Tree class inside the {}s, do you not have to use Tree:: in front of it? Is that because the function block is already shown to be from that same class?
And what if I have Tree::someFunction(){} and it uses a function or variable from a different class? Would I then have to put [other class's name]:: infront of the function or variable?
General question about using classes since I'm not completely comfortable with using them, yet: When you have something like Tree::Tree(){} or Tree::someFunction(){}, and it has a function or variable from the Tree class inside the {}s, do you not have to use Tree:: in front of it? Is that because the function block is already shown to be from that same class?
Yes, you do not have to always write "Tree::". Within the argument list and within the function body, the full scope should already be qualified. However, you are allowed to use the explicit name if you wish. There is no harm in doing so, and there may be times where you must use the full scope to avoid ambiguity (hopefully a rare problem).
The problem stems from how you called setX() and setY(). You have "int" within the parentheses, so the compiler thinks you were trying to write a function prototype and forgot the return type. Simple fix:
1 2 3 4 5
Tree::Tree(int x, int y){
setX(x); //This is how to call setX
setY(y);
}
Also, calling getX() and getY() has no effect, which is why I removed them.
Also, calling getX() and getY() has no effect, which is why I removed them.
Is that because you only use them when you need to call it, and here we're just setting the x and y coordinates values rather than using them?
My program now compiles successfully, but it doesn't seem like the plant_tree function works because it just prints out the default forest with a 4x10 of '-'s
Is that because you only use them when you need to call it, and here we're just setting the x and y coordinates values rather than using them?
Pretty much. Calling getX() and getY() like that is similar to:
1 2 3 4 5
int a(5), b(6);
a+b; //No effect. The values are added, but the result is not stored and not used.
int c = a+b; //Now the result is used.
Edit:
My program now compiles successfully, but it doesn't seem like the plant_tree function works because it just prints out the default forest with a 4x10 of '-'s
That is probably because the symbol wasn't actually changed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//This function doesn't actually modify your forest at all
void Forest::plant_tree(int x, int y){
Tree planting;
planting.setSymbol('*');
for(int row = 0; row < 4; row++){
for(int column = 0; column < 10; column++){
if(planting.getX() == column && planting.getY() == row){
cout << planting.getSymbol();
} else {
cout << " ";
}
}
cout << '\n';
}
}
What you can do is iterate over trees and look for a Tree object whose coordinates match what you're looking for. BUT, if you do it this way, this means you will have to set up the coordinates of all the Tree objects in the variable trees before calling plant_tree().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
void Forest::plant_tree(int x, int y){
for(std::size_t i(0); i < trees.size(); ++i){ //Go through each Tree
if(trees[i].getX() == x && trees[i].getY() == y){ //Do the coordinates match?
trees[i].setSymbol('*'); //Matches, so change the symbol
break; //Exit the loop since we already found the right Tree
}
}
}
//Edit: If you don't want to set up coordinates because you
// are sure of the Trees' order (that is the order they're in dictates
// where they are on the grid), you can use bit of math:
void Forest::plant_tree(int x, int y){
if(y*10 + x < 40){ //Make sure the coordinate is on the grid
trees[y*10 + x].setSymbol('*');
}
}
You are only printing the first row, four times. Look at how I accessed trees in order to change the symbol. You can use the same math in your print function.