Learning Classes

@RichardAmes, @HomineLudens, @Rakkachi

Glad to see these Q&A sessions are benefiting more than just the asker.
To be honest I wish we’d have more of these sorts of questions.

I enjoy explaining C++ features and discussing design approaches,
and it provides good information for future readers.

(We need more people out there experimenting with code and ending up with bugs for the forums to help fix.)

2 Likes

Any updates to this project or have you been too busy to work on it?

Have been a bit busy, so I havent had much time yet.

And I still dont fully understand the mechanics of C++, so I am looking at youtube tutorials to understand it better.

But I will continue, first step is trying out a really simple class setup just to know the syntax and structure better.

1 Like

Bitmap_Class_Call.zip (1.9 MB)

First simple setup for calling a class, this will only show the bitmap icon to see if i got this right.
That seems to work as intended.

Next, I would like to create a class object that consist out of the pokitto icon. To be continued…

Don’t be afraid to ask about the mechanics as well.

One of my preferred tutorial sites is http://www.learncpp.com/

It does indeed, althought it’s not particularly useful.

Try replacing myclass with this class and having a play around with it:

class Demo
{
public:
	Demo(void)
	{
		Pokitto::Display::println("Default Constructor");
	}
	
	Demo(const Demo & other)
	{
		Pokitto::Display::println("Copy Constructor");
	}
	
	// Note: needs C++11
	Demo(Demo && other)
	{
		Pokitto::Display::println("Move Constructor");
	}
	
	~Demo(void)
	{
		Pokitto::Display::println("Destructor");
	}
	
	Demo & operator=(const Demo & other)
	{
		Pokitto::Display::println("Copy Assignment");
		return *this;
	}
	
	// Note: needs C++11
	Demo & operator=(Demo && other)
	{
		Pokitto::Display::println("Move Assignment");
		return *this;
	}
};

This sort of setup helps to explain the rule of 3/5/0:

https://en.cppreference.com/w/cpp/language/rule_of_three

In 90% of cases you’ll be following the ‘rule of zero’.

Ok, little confused here… This doesnt look like the classes I have seen sofar in regards to the ()

Is this a different way of writing them ? Because this is how they look in my code : [quote=“Rakkachi, post:1, topic:1176”]
bool collidesWidth(GameObject *o)
{
// Hit test

}

[/quote]

I had a quick look here,

But although I get the general idea, there is still too much from c++ I dont know yet.

Ah, I was assuming you had been learning about the different kinds of constructors, but it looks like you’re only just learning the default and normal argument constructors.
In that case disregard the Demo class for now, but keep a copy to look at later…

1 Like

Bitmap_Class_Call_And_Pass_Valeu.zip (1.9 MB)

Yep, still learning the basics. My main problem is the understanding how to create and use a class or a object. The syntax is different in level than the vb I am used to.

To learn how to use c++ is very different, and some things are not fully explained depending on which website i check.

for example: I know that calling a class can create a object, but apparently this object only is present as long as that call is made? If that is thru then why is there a deconstructor ?

Lets say a want a balloon to appear whenever i press A, then when i release A the balloon should go up slowly and pop once it touches the screen edge. What I understand from C++ now this would require the balloon object to already be present but not visible. Pressing A only sets into motion what happens to the object, it does not create the object.

sorry in advance, these things must look really simple to you.

Here’s a hopefully slightly easier thing to follow.
Don’t worry about understanding the const, the use of & or the difference between -> and . for now, focus mainly on seeing how the objects are created and interracted with by the Demo function.

Please be aware that I’m using C++03 here because I noticed you’ve been using the online compiler.

C++03
// Needed for std::sqrt
#include <cmath>

class Vector
{
public:
	// Member variables
	int x;
	int y;
	
public:
	// Default constructor
	Vector(void)
	{
		this->x = 0;
		this->y = 0;
	}
	
	// Parameterised constructor
	Vector(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
	
	// Simple function
	double length(void) const
	{
		return std::sqrt((this->x * this->x) + (this->y * this->y));
	}
};

// Overload the == operator
inline bool operator==(Vector left, Vector right)
{
	return ((left.x == right.x) && (left.y == right.y));
}

class Point
{
public:
	// Member variables
	int x;
	int y;
	
public:
	// Default constructor
	Point(void)
	{
		this->x = 0;
		this->y = 0;
	}
	
	// Parameterised constructor
	Point(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
	
	// Overload the += operator
	Point & operator +=(Vector vector)
	{
		this->x += vector.x;
		this->y += vector.y;
		return *this;
	}
};

// Overload the == operator
inline bool operator==(Point left, Point right)
{
	return ((left.x == right.x) && (left.y == right.y));
}

void Demo(void)
{	
	// Initialised with parameters
	Vector offset = Vector(2, 1);
	Pokitto::Display::println(offset.x);
	Pokitto::Display::println(offset.y);
	
	// Get the length of the vector, roughly 2.236067977
	double offsetLength = offset.length();
	Pokitto::Display::println(offsetLength);
	
	// Default initialised
	// x = 0, y = 0
	Point point0 = Point();
	Pokitto::Display::println(point0.x);
	Pokitto::Display::println(point0.y);

	// Initialised with parameters
	Point point1 = Point(4, 5);
	Pokitto::Display::println(point1.x);
	Pokitto::Display::println(point1.y);
	
	// Uses the += operator, which modifies point1
	// x = 6, y = 6
	// Technically this is converted to point1.operator+=(offset);
	point1 += offset;
	Pokitto::Display::println(point1.x);
	Pokitto::Display::println(point1.y);
	
	if(point1 == Point(6, 6))
	{
		Pokitto::Display::println("Success");
	}
	else
	{
		Pokitto::Display::println("Failure");	
	}
}

If you’re compiling on EmBitz, here’s a version in C++11 style:

C++11
// Needed for std::sqrt
#include <cmath>

class Vector
{
public:
	// Member variables
	int x = 0;
	int y = 0;
	
public:
	// Default constructor
	Vector(void) = default;
	
	// Parameterised constructor
	Vector(int x, int y) : x(x), y(y) {}
	
	// Simple function
	double length(void) const
	{
		return std::sqrt((this->x * this->x) + (this->y * this->y));
	}
};

// Overload the == operator
inline bool operator==(Vector left, Vector right)
{
	return ((left.x == right.x) && (left.y == right.y));
}

class Point
{
public:
	// Member variables
	int x = 0;
	int y = 0;
	
public:
	// Default constructor
	Point(void) = default;
	
	// Parameterised constructor
	Point(int x, int y) : x(x), y(y) {}
	
	// Overload the += operator
	Point & operator +=(Vector vector)
	{
		this->x += vector.x;
		this->y += vector.y;
		return *this;
	}
};

// Overload the == operator
inline bool operator==(Point left, Point right)
{
	return ((left.x == right.x) && (left.y == right.y));
}

void Demo(void)
{	
	// Initialised with parameters
	Vector offset = Vector(2, 1);
	Pokitto::Display::println(offset.x);
	Pokitto::Display::println(offset.y);
	
	// Get the length of the vector, roughly 2.236067977
	double offsetLength = offset.length();
	Pokitto::Display::println(offsetLength);
	
	// Default initialised
	// x = 0, y = 0
	Point point0 = Point();
	Pokitto::Display::println(point0.x);
	Pokitto::Display::println(point0.y);

	// Initialised with parameters
	Point point1 = Point(4, 5);
	Pokitto::Display::println(point1.x);
	Pokitto::Display::println(point1.y);
	
	// Uses the += operator, which modifies point1
	// x = 6, y = 6
	// Technically this is converted to point1.operator+=(offset);
	point1 += offset;
	Pokitto::Display::println(point1.x);
	Pokitto::Display::println(point1.y);
	
	if(point1 == Point(6, 6))
	{
		Pokitto::Display::println("Success");
	}
	else
	{
		Pokitto::Display::println("Failure");	
	}
}

Which sorts of things?

Not quite.

Calling the constructor creates an object. You can think of a constructor as a special function that creates, initialises and returns an object.
If that object is assigned to a variable or argument, it will live as long as the variable/argument.
When the variable/argument falls out of scope, the object is destroyed by calling its destructor.

Example
// Default constructed when the program starts
// Destructed only when the program ends
SomeClass globalA; 

void SomeFunction(void)
{
	// Object is constructed and assigned to a
	SomeClass a = SomeClass();
	
	// Object is constructed and assigned to b
	SomeClass b;
	
	{
		// Object is constructed and assigned to c
		SomeClass c = SomeClass();
		
		// Scope ends, c is destructed
	}
	
	// Scope ends, b is destructed and then a is destructed
}

For most classes, the destructor is empty, only special classes (like data structures and file handles) need a non-empty destructor.

Sort of.

Essentially you would have to already have somewhere to store the object.
If that somewhere is in a variable then that variable would be initialised with an object (usually with the object’s default constructor if it has one), so from that point of view the analogy is somewhat true.
To get around that you’d need a way to symbolise whether the balloon has been activated.

However there are other places that objects can be stored.
Understanding ‘where’ that is requires explaining dynamic memory, which you might not be ready for - especially if you don’t understand pointers yet.

However, I can explain the difference in an abstract way without explaining dynamic allocation.
std::vector uses dynamic allocation to store its objects.
So if you had a std::vector<Balloon> to store your balloon object in, it would start without any objects.
So if instead, pressing A added a new balloon to that vector, the object would be created and stored in the vector.
Then you would be able to look at the vector each frame and operate on any balloons inside the vector.
When a balloon goes offscreen, you remove it from the vector.

Also remember that an object is just a bundle of data, it is how you choose to interpret and act upon that data that causes it to act like a real object in a game.
For example, in my Physix demo I have a function that simulates the phyiscs.
Without this my objects don’t really do much.

To put icing on the cake, here’s an exaple of part of the balloon program, I’ll leave you to do the drawing and integrate it into something the Pokitto can run.
I haven’t tested if the code compiles (I literally just threw it together in Notepad++), so let me know if you run into issues.

C++03
#include <vector>
#include <cstdlib>

class Balloon
{
public:
	int x;
	int y;
	
public:
	Balloon(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
};

std::vector<Balloon> vector;

void updateBalloons()
{
	// Note: this is much easier with C++11
	for (std::vector<Balloon>::iterator iterator = std::begin(vector); iterator != std::end(vector);)
	{
		// iterator behaves like a pointer
		
		// Move balloon up
		--iterator->y;
		
		// Check if the balloon is offscreen
		if(iterator->y < 0)
		{			
			// Balloon is offscreen, remove it
			iterator = vector.erase(iterator);
		}
		else
		{
			// Else move to the next balloon
			++iterator;
		}
	}
}

void handleInput()
{
	if(Pokitto::Buttons::held(BTN_A, 1))
	{
		int x = std::rand() % 220;
		vector.push_back(Balloon(x, 176));
	}
}

And a C++11-ified version:

C++11
#include <vector>
#include <algorithm>
#include <iterator>
#include <random>

class Balloon
{
public:
	int x;
	int y;
	
public:
	Balloon(int x, int y) : x(x), y(y) {}
};

std::vector<Balloon> vector;
// would be able to seed from the Pokitto's inner state if one of my proposals is implemented:
// https://talk.pokitto.com/t/suggestion-future-of-pokittolib-v2-0/1003/17
minstd_rand0 generator = minstd_rand0(100);

void updateBalloons()
{
	// Remove all out of bounds balloons
	// This is actually quite complicated
	vector.erase(std::remove_if(std::begin(vector), std::end(vector), [](const Balloon & balloon) { return ballon.y < 0; }, std::end(vector));
	
	for(auto & balloon : vector)
		--vector[index].y;
}

void handleInput()
{
	if(Pokitto::Buttons::held(BTN_A, 1))
	{
		auto x = generator() % 220;
		vector.push_back(Balloon(x, 176));
	}
}

Yes and no.

They are simple in that I already know how they work in detail,
but no because C++ is a complicated language and there’s a lot to learn.

I didn’t learn it overnight, I started 5-6 years ago and took it a day at a time.
The key to understanding C++ is to understand what goes on behind the scenes and to understand the rules.
A lot of the time when writing C++ I’m thinking about how memory is allocated and how the compiler interprets what I’m writing.


And to cap off an overly-long post, here’s an image my friend sent me earlier:

The rake is probably a missing semicolon.

4 Likes