Using multiple classes in separate files?

Pages: 12
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):

tree.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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 



tree.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "tree.h"
#include <iostream>


using namespace 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();
}



forest.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef _FORESTH
#define _FORESTH

#include <vector>
#include "tree.h"

class Forest {
  public:
    Forest();

    void plant_tree(int x, int y);
    void cut_tree(int x, int y);
    void print();

  private:
    std::vector<Tree> trees;
};

#endif 



forest.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "tree.h"
#include "forest.h"
#include <iostream>
#include <vector>

using namespace std;


vector< vector<char> > the_forest;	// Declaring the multidimensional vector holding the forest


Forest::Forest(){	// Defines default forest!

	vector< vector<char> > the_forest (4, vector<char> (10,'-'));	// INITIALIZING FOREST: Constructor creates default forest, consisting of entirely soil

}


void Forest::plant_tree(int x, int y){
	Tree planting();
	planting.setSymbol('*');

	the_forest[y][x] = planting.getSymbol();
}

void Forest::cut_tree(int x, int y){
	Tree cutting();
	cutting.setSymbol('-');

	the_forest[y][x] = cutting.getSymbol();
}

void Forest::print(){

	for(int row = 0; row < 4; row++){
		for(int column = 0; column < 10; column++){
			cout << the_forest[row][column] << " ";
		}
		cout << "\n";
	}


}



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>

using namespace 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?
Last edited on
Line 11 in main.cpp:
http://en.wikipedia.org/wiki/Most_vexing_parse


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());
}
Last edited on
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.
Last edited on
Find from trees the element t that has t.getX()==x && t.getY()==y. If there is such t, then change its symbol.

Now you should notice that plant_tree does not have to append new tree, if it already has one with (x,y). It just ensures that that tree becomes '*'.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for ( int row = 0; row < 4; ++row )
{
  for ( int column = 0; column < 10; ++column )
    {
      if ( trees has t, where t.getX()==column && t.getY()==row )
        {
          cout << t.getSymbol();
        }
      else
        {
          cout << ' ';
        }
    }
  cout << '\n';
}

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.

Believe it or not:
9
10
11
12
13
14
15
16
vector< vector<char> > the_forest;	// Declaring the multidimensional vector holding the forest


Forest::Forest(){	// Defines default forest!

	vector< vector<char> > the_forest (4, vector<char> (10,'-'));	// INITIALIZING FOREST: Constructor creates default forest, consisting of entirely soil

}

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.
Last edited on
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.

I changed my code to this:

forest.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef _FORESTH
#define _FORESTH

#include <vector>
#include "tree.h"

class Forest{
  public:
    //inits a forest without any tree
    Forest();
    //
    void plant_tree(int x, int y);
    void cut_tree(int x, int y);
    void print();

  private:
    std::vector<Tree> trees;
};

#endif 



forest.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "tree.h"
#include "forest.h"
#include <iostream>
#include <vector>

using namespace std;


Forest::Forest(){	// Creates default forest!

	vector<Tree> Forest::trees(40,'-');

}


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';
	}
}

void Forest::cut_tree(int x, int y){
	Tree cutting;
	cutting.setSymbol('-');

	for(int row = 0; row < 4; row++){
		for(int column = 0; column < 10; column++){
			if(cutting.getX() == column && cutting.getY() == row){
				cout << cutting.getSymbol();
			} else {
				cout << " ";
			}
		}
		cout << '\n';
	}
}

void Forest::print(){

	for(int row = 0; row < 4; row++){
		for(int column = 0; column < 10; column++){
			cout << trees[column] << " ";
		}
		cout << '\n';
	}


}


tree.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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 



tree.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "tree.h"
#include <iostream>


using namespace 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>

using namespace 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
{}
I just realized I declared vector<Tree> trees as a vector of char instead of Tree objects. I'm really not sure where to go from here.
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.
you'll probably have to do some quick loops to set up the coordinates.

What do you mean by setting up the coordinates?
Also, is it improper for me to do this?:

In tree.cpp:
1
2
3
4
5
Tree::Tree(){

	void Tree::setSymbol('-');     // Default symbol

}


In forest.cpp:
1
2
3
4
5
Forest::Forest(){	// Creates default forest!
	
	vector<Tree> Forest::trees(40, Tree treeObject);

}
Last edited on
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.
Also, is it improper for me to do this?


You can take a look at my previous post:
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

}


Should really look like:
1
2
3
4
5
6
7
8
9
Tree::Tree()
    : symbol('-')
{}

//Alternatively

Tree::Tree(){
    setSymbol('-');
}


And
1
2
3
4
5
Forest::Forest(){	// Creates default forest!
	
	vector<Tree> Forest::trees(40, Tree treeObject);

}

Should look like:
1
2
3
4
5
6
7
8
9
Forest::Forest()
    : trees(40, Tree()) //Create 40 default Tree objects
{}

//Alternatively

Forest::Forest(){
    trees.resize(40, Tree());
}
Last edited on
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?

And I'm getting an error with this part:
1
2
3
4
5
6
7
8
9
Tree::Tree(int x, int y){

	setX(int x);
	setY(int y);

	getX();
	getY();

}

saying expected primary-expression before 'int'
Why is that?
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).

1
2
3
4
5
6
7
8
9
Tree::Tree(int x, int y){

	setX(int x);
	setY(int y);

	getX();
	getY();

}

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.
Last edited on
Alrighty, thanks! :D

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
Last edited on

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('*');
    }
}
Last edited on
I used:
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('*');
}
}


But, I'm still getting a 4x10 of '-'s...

Does it have to do with my print function?
1
2
3
4
5
6
7
8
9
10
11
void Forest::print(){

	for(int row = 0; row < 4; row++){
		for(int column = 0; column < 10; column++){
			cout << trees[column].getSymbol() << " ";
		}
		cout << '\n';
	}


}

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.
Pages: 12