Jump to content
  • Advertisement
Sign in to follow this  
MrCpaw

Private and Classes

This topic is 3463 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I had a quick question on why I should use my PlayerHealth, PlayerDamage, ect... under private in my class? I've been told the proper way for OOP is to have every variable you set under private and use functions under public to use them. I know how to use pointers and pass references to a function, ect... However I wrote this code:
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;

class Player // Player Class
{
public:

	string PlayerName; // Player Name
	int PlayerHealth; // Player Health
	int PlayerDamage; // Player Damage Power
	int PlayerGold; // Gold Obtained
	int PlayerExp; // Experince Obtained

	// Constructor
	Player(string PlayerName_TEMP, int PlayerHealth_TEMP, int PlayerDamage_TEMP, int PlayerGold_TEMP, int PlayerExp_TEMP):
	  PlayerName(PlayerName_TEMP), PlayerHealth(PlayerHealth_TEMP), PlayerDamage(PlayerDamage_TEMP), PlayerGold(PlayerGold_TEMP), PlayerExp(PlayerExp_TEMP)
	  {}
};

int main()
{
	// Test
	cout << "Testing...\n\n\n";

	// Make Player
	Player PlayerOne("MrCpaw", 100, 5, 0, 0);

	cout << PlayerOne.PlayerName;

	cin.get();
	return 0;
}

Now this works fine and I can change anything I want, however I was told the right way is to write is like this:
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;

class Player // Player Class
{
private:

	string PlayerName; // Player Name
	int PlayerHealth; // Player Health
	int PlayerDamage; // Player Damage Power
	int PlayerGold; // Gold Obtained
	int PlayerExp; // Experince Obtained

public:

	// Constructor
	Player(string PlayerName_TEMP, int PlayerHealth_TEMP, int PlayerDamage_TEMP, int PlayerGold_TEMP, int PlayerExp_TEMP):
	  PlayerName(PlayerName_TEMP), PlayerHealth(PlayerHealth_TEMP), PlayerDamage(PlayerDamage_TEMP), PlayerGold(PlayerGold_TEMP), PlayerExp(PlayerExp_TEMP)
	  {}
};

int main()
{
	// Test
	cout << "Testing...\n\n\n";

	// Make Player
	Player PlayerOne("MrCpaw", 100, 5, 0, 0);

	cout << PlayerOne.PlayerName; // ERROR CANNOT USE PRIVATE

	cin.get();
	return 0;
}

However I'm unable to use it. I did make a pointer and even with a pointer I couldn't use PlayerOne->PlayerName to do anything... I'm confused on why I'm supposed to do this and how this is supposed to work in private so when PlayerOne goes into a battle I can keep adding Gold, Exp, and dealing with health. I've mainly programmed in VB and only used everything under a global scope, yes I know this is against OOP. I need some help understanding why this is a better way and how I can still use it just as good. Thanks for any help you guys can provide!

Share this post


Link to post
Share on other sites
Advertisement
By making those members private, what you've done is ensured that those members cannot be accessed directly (except by methods of your Player class). This is true whether its a pointer or not.

So, this will fail:

Player PlayerOne("MrCpaw", 100, 5, 0, 0);
cout << PlayerOne.PlayerName;

And so will this:

Player *PlayerOne = new Player("MrCpaw", 100, 5, 0, 0);
cout << PlayerOne->PlayerName;


In order to access the player name for this purpose, you will actually need to create a method in your Player class, called GetPlayerName or something similar, in the public scope, and have it return the string:


class Player // Player Class
{
private:

string PlayerName; // Player Name
int PlayerHealth; // Player Health
int PlayerDamage; // Player Damage Power
int PlayerGold; // Gold Obtained
int PlayerExp; // Experince Obtained

public:

// Constructor
Player(string PlayerName_TEMP, int PlayerHealth_TEMP, int PlayerDamage_TEMP, int PlayerGold_TEMP, int PlayerExp_TEMP):
PlayerName(PlayerName_TEMP), PlayerHealth(PlayerHealth_TEMP), PlayerDamage(PlayerDamage_TEMP), PlayerGold(PlayerGold_TEMP), PlayerExp(PlayerExp_TEMP)
{}

// Methods
GetPlayerName() { return PlayerName; }
};



And then, to print the name out, you would do this:

Player PlayerOne("MrCpaw", 100, 5, 0, 0);
cout << PlayerOne.GetPlayerName();

You may be confused as to why C++ programmers bother to do this. I admit I am not as well-versed in OOP principles as I'd like to be and so my answer will probably not be sufficient, but here it goes: it's often a good practice to do this because it provides separation between the interface of your class and the implementation of your class.

Often times, from the perspective of other programmers who are using the class you wrote, it's often easier to deal with simple method calls than it is to try to access the data directly. This is hard to see what an example as trivial as this, because PlayerOne.PlayerName seems just as easy, if not easier, than PlayerOne.GetPlayerName(). However, for lots of data, it's not.

Not only that, but by forcing the user of your class to go through a method like this (it's called an accessor method), you give yourself the ability to change the way that the data is set up in your class without breaking all of the code.

For example (and this is a terrible example that would never happen in real life, but bear with me), but suppose that you wanted to change PlayerName to a character array instead of a string object. Imagine doing this after your game code already contains a couple hundred references to PlayerName, like this:

std::string strValue = PlayerOne.PlayerName.substr(5);

Now, if you changed PlayerName to a character array instead of a string object, then the above line of code, and every line of code like it in your whole project, would result in a compile error.

On the other hand, if those lines of code looked something like this:

std::string strValue = PlayerOne.GetPlayerName().substr(5);

Then all you would have to do, after changing PlayerName to a character array, is change the GetPlayerName() method as follows:

GetPlayerName() { return string(PlayerName); }

Now, GetPlayerName() returns PlayerName in string form even though its a character array, and all of your other code compiles as normal.

This example is silly, but it basically conveys the point that, by having users of your class go through accessor methods and such, you provide an interface to your class, and this interface sort of acts like a layer of separation...creating a sort of "outside shell" to your class, which allows you to make changes to the internal details of your class, and as long as you update the interface to handle it, you allow users of your class to remain ignorant of the changes.

Share this post


Link to post
Share on other sites
Thanks for the great response. I do find using the private way a pain in the butt, only because I'm new to OOP.

I rewrote the code and it works fine.


#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;

class Player // Player Class
{
private:
string PlayerName; // Player Name
int PlayerHealth; // Player Health
int PlayerDamage; // Player Damage Power
int PlayerGold; // Gold Obtained
int PlayerExp; // Experince Obtained

public:

// Constructor
Player(string PlayerName_TEMP, int PlayerHealth_TEMP, int PlayerDamage_TEMP, int PlayerGold_TEMP, int PlayerExp_TEMP):
PlayerName(PlayerName_TEMP), PlayerHealth(PlayerHealth_TEMP), PlayerDamage(PlayerDamage_TEMP), PlayerGold(PlayerGold_TEMP), PlayerExp(PlayerExp_TEMP)
{}

// Make Name
string PlayerMakeName()
{
return PlayerName;
}

};

int main()
{
// Test
cout << "Testing...\n\n\n";

// Make Player
Player PlayerOne("Christopher", 100, 5, 0, 0);

cout << PlayerOne.PlayerMakeName();

cin.get();
return 0;
}



string PlayerMakeName() works fine to display the name that is set per instance of the object.

Thanks! :)

Share this post


Link to post
Share on other sites
Edit: No problem! I wrote the following before I read your response, but it might help make things clearer anyway:




Here's an example that is probably a bit more realistic.

Suppose that you originally set up the class like this (ignoring variables that aren't relevant to the discussion):


class Player
{
public:
string PlayerName;

// Constructor
Player(string PlayerName_TEMP) : PlayerName(PlayerName_TEMP) {}
};




And all over your code, you have lines of code like this:

Player PlayerOne("Bill Baxter");
cout << PlayerOne.PlayerName << endl;


This will work fine, but what if, much later on, you decided that you wanted to store the first name and surname as separate strings?


class Player
{
public:
string FirstName, LastName;

// Constructor
Player(string FirstName_TEMP, string LastName_TEMP) : FirstName(PlayerName_TEMP), LastName(PlayerName_TEMP) {}
};




Now, you have a whole bunch of code that references PlayerName, which no longer exists, and thus a whole bunch of compiler errors.

On the other hand, if you had originally set up the class like this:


class Player
{
private:
string PlayerName;

public:
// Constructor
Player(string PlayerName_TEMP) : PlayerName(PlayerName_TEMP) {}

string GetPlayerName() { return PlayerName; }
};




Then users of your class would be forced to get the player name thusly:

Player PlayerOne("Bill Baxter");
cout << PlayerOne.GetPlayerName() << endl


And now, if you want to change the class such that it stores the first name and last name separately, you can do so:


class Player
{
private:
string FirstName, LastName;

public:
// Constructors
Player(string PlayerName_TEMP)
{ /* Add code for splitting the names here */ }

string GetPlayerName() { return FirstName + LastName; }
};




Now, you were able to change the way the class works internally while keeping the interface exactly the same. Like the comment in the constructor says, you can even rework the constructor to take the full name (so that you don't invalidate all the intializations of all of the Player objects created in your code) and split it up.

Share this post


Link to post
Share on other sites
Thanks again for all this information. I guess there is a long road ahead for me to fully take advantage of OOP. Just a matter of getting use it and more broader thinking to how I can really use OOP to display my private info.

I never new about making a function to display a name until today. Now I can use what I've learned to deal with Health and Gold, ect...

Thanks again, your help really allowed me to fully understand how to use Private without too much hassle.

Share this post


Link to post
Share on other sites
Quote:
Original post by MrCpaw
Hello, I had a quick question on why I should use my PlayerHealth, PlayerDamage, ect... under private in my class? I've been told the proper way for OOP is to have every variable you set under private and use functions under public to use them.


That is the general approach, but in this case, a little knowledge is a very dangerous thing.

Quote:
However I'm unable to use it.


That is the point.

Quote:
I did make a pointer and even with a pointer I couldn't use PlayerOne->PlayerName to do anything...


That changes nothing, in the same way that knocking on a door when noone's home does nothing to unlock it.

Quote:
I'm confused on why I'm supposed to do this


For the same reason that you do not pay for a purchase by reaching across the cashier's desk and into the till, and making change for yourself.

Quote:
and how this is supposed to work in private


It doesn't; the work is done by public functions. Private data just remembers stuff.

Quote:
so when PlayerOne goes into a battle I can keep adding Gold, Exp, and dealing with health.


The thing is, those aren't the tasks you want to perform. You don't inspect the character's current HP, perform arithmetic and reset them; you cause the player to take damage.

You can even make some "helper" private member functions. This is because it doesn't make sense for calling code to do this work directly, but they represent a portion of the work that has to be done.

Example: The player might take damage or be healed. In either case, we want to ignore any effect that is supposed to cause or heal a negative amount of damage (maybe a weapon's effectiveness was reduced past zero, and we don't want to worry about it at that point in the calculation).

Both of these tasks adjust the player's HP, by a negative or positive amount respectively. We can use an internal "adjust hp" function to apply the adjustment - which also needs to respect lower (zero) and upper (current max HP) limits on the player's HP. Then in each public function, we insert the logic to avoid negative damage/healing, and translate damage into a "negative HP adjustment".

We should also be able to check if the player is dead, i.e. if he's reached zero HP. We won't worry about checking his exact HP count for now; the only reason we're likely to want to do that is to display the player's status, and there are more complicated design decisions to be made there.


// I assume private member variables named 'hp' and 'max_hp'.
// Notice that I never include the class name as a part of the name of the
// data members, because this *never actually gives us any more information*.
// We already know what class a data member is part of, in every case.

// std::max and std::min are provided by <algorithm>. They return the maximum
// and minimum, respectively, of two values - a very useful pair of utility
// functions for "clamping" values.

// These member functions are public:
void Player::TakeDamage(int amount) {
// I like to use a different naming convention for public vs. private
// member functions.
adjust_hp(-std::max(0, amount));
}

void Player::Heal(int amount) {
adjust_hp(std::max(0, amount));
}

// This one is private:
void Player::adjust_hp(int amount) {
// Subtract the damage and apply limits. (The max_hp check is used to handle
// the case of healing past the maximum.)
// Notice how I use std::min and std::max together to restrict the result
// to a range.
hp = std::min(std::max(0, hp + amount), max_hp);
}

Share this post


Link to post
Share on other sites
Thanks for the responses! I'm starting to understand this stuff, I hope!

So what I understand so far is that all Private variables are used with functions within their class. This allows these variables to remain private to every other class, ect... and can only be changed within it's own class. In order to change PlayerGold for example you would need to create a function within that same class that deals directly with that private variable.

So I made a quick test to see if this worked right:


#include <iostream>

using namespace std;

// Person
class Person
{
private:

// Age
int PersonAge;

public:

// Construtor
Person(int PersonAge_TEMPCONT):
PersonAge(PersonAge_TEMPCONT)
{

}

// Set a New Person's Age
void SetPersonAge(int PersonAge_TEMP)
{
PersonAge = PersonAge_TEMP;
}

// Show Current Age
int ShowPersonAge()
{
return PersonAge;
}
};

int main()
{
Person Bob(20);

// How Old is Bob?
cout << Bob.ShowPersonAge();

// Change Age to 50
Bob.SetPersonAge(50);
cout << "\n\n" << Bob.ShowPersonAge();

// Display again to show it really changed
cout << "\n\n\n";
cout << Bob.ShowPersonAge();

cin.get();
return 0;
}



Now that I understand how to do this, all I need to do now is setup a way to use Bob.ShowPersonAge() within other functions. I know how to use pointers and pass them to a function and have the vaule really changed, however I wasn't able to succeed with setting the age into another function for Bob.ShowPersonAge() the same way I could normally do with pointers.

I have no idea how to use pointers with functions such as Bob.ShowPersonAge() and change the value in another area out of scope.

I did try the Person* Bob = new Person(20); way but couldn't use the functions within any other function when passing it.

Now mabye I'm getting really confused... But what I'm trying to do is take the value from ShowPersonAge() in that function and use that public function in another function.

I need some insight on how to go about doing this.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!