Question about classes with other classes as member data.

I am currently trying to make my way through this book "Sam's Teach Yourself C++ in 24 hrs" and am confused about a certain example program. The program is supposed to find the area of a rectangle as well as the x/y coordinate(s) of the corners of said rectangle. I am specifically confused about what is going on in the setter functions for the corners and top/bottom/right etc. Any input would be greatly appreciated, thanks! I will paste the header file first and the .cpp after.

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
 #include <iostream>

class Point
{
	//no constructor, use default
public:
	void setX(int newX) { x = newX; }
	void setY(int newY) { y = newY; }
	int getX() const { return x; }
	int getY() const { return y; }
private: 
	int x;
	int y;
};

class Rectangle
{
public:
	Rectangle(int newTop, int newLeft, int newBottom, int newRight);
	~Rectangle() {}
	
	int getTop() const { return top; }
	int getLeft() const { return left; }
	int getBottom() const { return bottom; }
	int getRight() const { return right; }
	
	void setTop (int newTop);
	void setLeft (int newLeft);
	void setBottom (int newBottom);
	void setRight ( int newRight);
	
	Point getUpperLeft() const { return upperLeft; }
	Point getLowerLeft() const { return lowerLeft; }
	Point getUpperRight() const { return upperRight; }
	Point getLowerRight() const { return lowerRight; }
	
	void setUpperLeft(Point Location);
	void setLowerLeft(Point Location);
	void setUpperRight(Point Location);
	void setLowerRight(Point Location);
	
	int getArea() const;

private:
	Point upperLeft;
	Point upperRight;
	Point lowerLeft;
	Point lowerRight;
	int top;
	int left;
	int bottom;
	int right;
};

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include "Rectangle.hpp"

Rectangle::Rectangle(int newTop, int newLeft, int newBottom, int newRight)
{
	top = newTop;
	left = newLeft;
	bottom = newBottom;
	right = newRight;
	
	upperLeft.setX(left);
	upperLeft.setY(top);
	
	upperRight.setX(right);
	upperRight.setY(top);
	
	lowerLeft.setX(left);
	lowerLeft.setY(bottom);
	
	lowerRight.setX(right);
	lowerRight.setY(bottom);
}

void Rectangle::setUpperLeft(Point location)
{
	upperLeft = location;
	upperRight.setY(location.getY());
	lowerLeft.setX(location.getX());
	top = location.getY();
	left = location.getX();
}

void Rectangle::setLowerLeft(Point location)
{
	lowerLeft = location;
	lowerRight.setY(location.getY());
	upperLeft.setX(location.getX());
	bottom = location.getY();
	left = location.getX();
}

void Rectangle:: setLowerRight(Point location)
{
	lowerRight = location;
	lowerLeft.setY(location.getY());
	upperRight.setX(location.getX());
	bottom = location.getY();
	right = location.getX();
}

void Rectangle::setUpperRight(Point location)
{
	upperRight = location;
	upperLeft.setY(location.getY());
	lowerRight.setX(location.getX());
	top = location.getY();
	right = location.getX();
}

void Rectangle::setTop(int newTop)
{
	top = newTop;
	upperLeft.setY(top);
	upperRight.setY(top);
}

void Rectangle::setLeft(int newLeft)
{
	left = newLeft;
	upperLeft.setX(left);
	lowerLeft.setX(left);
}

void Rectangle::setBottom(int newBottom)
{
	bottom = newBottom;
	lowerLeft.setY(bottom);
	lowerRight.setY(bottom);
}

void Rectangle::setRight(int newRight)
{
	right = newRight;
	upperRight.setX(right);
	lowerRight.setX(right);
}

int Rectangle::getArea() const
{
	int width = right - left;
	int height = top - bottom;
	return (width * height);
}

//compute area of the rectangle by finding corners,
//establish width and height and then multiply
int main()
{
	//initalize a local Rectangle variable
	Rectangle myRectangle(100, 20, 50, 80);
	
	int area = myRectangle.getArea();
	
	std::cout << "Area: " << area << std::endl;
	std::cout << "Upper Left X coordinate: ";
	std::cout << myRectangle.getUpperLeft().getX() << std::endl;
	return 0;
}


The output looks like this
Area: 3000
Upper Left X coordinate: 20
Last edited on
It's not easy to guess what it's not clear to you, but I hope this could be of any help:

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
// The rectangle sides are supposed to be parallel to x and y axes.

void Rectangle::setUpperLeft(Point location)
    // location is an instance of class Point.
    // Class Point provides an x and a y coordinates, which are private.

    upperLeft = location; // I think this is clear.
    
    // The upperRight corner must be as 'high' as upperLeft corner.
    // So it can uses the same 'y' than upperLeft, even if we don't know its
    // 'x' yet.
    upperRight.setY(location.getY());   // we need to access Point properties
                                        // by getters because they are private.
    
    // lowerLeft corner must be somewhere under upperLeft corner.
    // So we can use upperLeft 'x' coordinate even if we can't set the 'y' yet.
    lowerLeft.setX(location.getX());

    // 'top' is the upper side of the rectangle; it's supposed to be parallel
    // to the x-axis. So, every time we have a new position for the upperLeft
    // or upperRight corner, we can update its position (no matter haw long it is).
    // Geometrically its position is y = a number.
    top = location.getY();
    
    
    // 'left' represents the left side of the rectangle; it's supposed to be 
    // parallel to the y-axis. So, every time we have a new position for the 
    // upperLeft or lowerLeft corner, we can update its position 
    // (no matter haw long it is).
    // Geometrically its position is x = a number.
    left = location.getX();
}

First off I would like to say thank you for replying, and I appreciate you taking the time to help me. I think I understand everything your are saying, I am just confused why we even need the setter functions at all. At the beginning of the Rectangle constructor definition, all of the private Point variables (upperLeft, upperRight, etc.) are given X and Y values using the setX and setY functions from the Point class member functions with statements like upperLeft.setX(left) right? I am still confused as to when these setter functions take effect. In int Main() they have the statement myRectangle.getUpperLeft().getX() , would this statement not work without the setter functions? Sorry I am new to programming and have never dealt with classes.
After replying I just went into the cpp file and literally deleted all of the setter functions and it still works the same.
Since x/y, and top/left/etc. are private, you cannot access them outside of the class itself. That said, for classes like point it probably is not worth it in practice since the member functions aren't helping you at all; they aren't checking for invalid data or anything.
Last edited on
literally deleted all of the setter functions and it still works the same

That’s quite a common experience: the examples written to explain programming principles are often redundant. They are conceived to be clear, more than concise. You are doing right, playing with code: it’s probably what the author hopes you would do.
So, even if you’ve already got the proper explanation by firedraco, I invite you to go on playing with the code: instead of deleting setters, use them:
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
int main()
{
    //initalize a local Rectangle variable
    Rectangle myRectangle(100, 20, 50, 80);
    
    int area = myRectangle.getArea();
    
    std::cout << "Area: " << area << std::endl;
    std::cout << "Upper Left X coordinate: ";
    std::cout << myRectangle.getUpperLeft().getX() << std::endl;

    myRectangle.setTop(13);
    myRectangle.setLeft(566);
    myRectangle.setBottom(3);
    myRectangle.setRight(666);

    int area = myRectangle.getArea();
    
    std::cout << "New area: " << area
              << "\nUpper Left X and Y coordinate: "
              << myRectangle.getUpperLeft().getX() << ' '
              << myRectangle.getUpperLeft().getY() << '\n';

    return 0;
}


You could try to guess what it does and then run it.
(please, note: I didn’t test it)
Yeah that is what I was going to do next. I understand how to use the setTop setRight functions because like you show above, they only take one integer argument. If I were to want to test the setUpperRight etc. functions would I put in two arguments because it takes a Point-datatype argument? like setUpperRight(5,5)? Thanks alot for all the input guys, my first time posting on this site and it has proven to be very helpful!
I am still confused as to when these setter functions take effect.

They take effect when you call them. Keep in mind that in a C++ program, execution goes from one step to the next to the next in a (mostly) defined order. It isn't like a spreadsheet where you put in a bunch of formulas and the spreadsheet figures out how to evaluate them until everything is consistent.

In this example, the setters aren't called but you can still look at them to understand them. The key is to realize that the class is storing a whole lot of redundant data. All it really needs to define the rectangle are left, top, bottom and right. It stores the 4 private points so it can quickly return them in getUpperRight(), getUpperLeft() etc. The alternative would be to construct new points at runtime from the 4 edges. For example getUpperLeft() could be implemented as
1
2
3
4
5
6
getUpperLeft() {
    Point result;
    result.setX(left);
    result.setY(top);
    return result;
}


But again, the point here is that there is redundant data in the class.

So what should Rectangle::setUpperLeft() do? It must change the upperLeft point member, but that would leave the top and left members with the old values. So those must change too. And if you change the upper left corner, then the lower left corner must move also, so you change that as well.

By the way, this is an interesting example, but you should avoid storing redundant data in practice. The problem is that it's too easy to forget to update all data everywhere you need to, so it's a recipe for bugs. Do it if you really have to (usually for performance) but be careful when you do.
Ah I see, I think understand these setter functions now. I think this book should have said at least a line or two about why these setter functions were included but they kind of skipped over them in the description of what the code did. Luckily I found this forum so now I understand and can move onto the next chapter. Thank you to everyone who took the time to help me out
Topic archived. No new replies allowed.