Initialize class variable by passing a reference to it - c++

I have a quite interesting and funny C++ exercise:
This the imposed main:
int main(void)
{
Exam e = Exam(&Exam::cheat);
e.kobayashiMaru = &Exam::start;
(e.*e.kobayashiMaru)(3);
Exam::cheat = true;
if (e.isCheating())
(e.*e.kobayashiMaru)(4);
return (0);
}
And this is the asked output:
[The exam is starting]
3 Klingon vessels appeared out of nowhere.
You lost again.
[The exam is starting]
4 Klingon vessels appeared out of nowhere.
Win !
You now need to create the Exam class to get the correct output.
Here is what I've done:
class Exam
{
public:
Exam(bool *_cheat);
typedef void (Exam::*func)(int);
void start(int);
bool isCheating();
static bool cheat;
func kobayashiMaru;
};
I encounter problems with the Exam(&Exam::cheat) thing. What I understand so far is that Exam is taking the address of it's own cheat variable. When entering the Exam's constructor cheat is uninitialized. So for me I would initialize it here with false.
Exam::Exam(bool * _cheat)
{
*_cheat = false;
}
But by doing this I get a multiple definition with Exam(&Exam::cheat). I'm not sure about my reflection, can maybe someone enlighten me on what's really happening here?

Change:
static bool cheat;
to:
bool cheat;
inside your Exam class to allow every new object handle its own cheat variable with its own value.
Constructor will initialize your cheat variable by given value while you creating new object with constructor like this:
Exam::Exam(bool isCheating)
{
this->cheat = isCheating;
}
Or if you want to initialize cheat variable as false/true by default you can make constructor like this:
Exam::Exam()
{
cheat = false;
//or cheat = true;
}
You can handle multiple constructors too.
Now to create new object from Exam class use:
Exam *exam1 = new Exam(true); //Will initialize cheat as false
Exam *exam2 = new Exam(false); //Will initialize cheat variable as true
Then access your methods inside exam1 and exam2 object like this:
exam1->methodName(1243);
exam2->secondMethodName(true);
exam2->thirdMethodName();
exam3->addFriend(&exam1);
Those ones are just examples.
Hope I understood your problem. :)

You have a few issues (described in comments below)
class Exam
{
public:
// If cheat is supposed to be a member variable assigned to each instance, passing a pointer to itself is silly and unnecessary
// If cheat is supposed to be shared between all instances of exam, passing a pointer to itself is silly and unnecessary
Exam(bool *_cheat);
static bool cheat;
// these two declarations are unnecessary. They do nothing but add complexity where it is not needed
typedef void (Exam::*func)(int);
func kobayashiMaru;
void start(int);
bool isCheating();
};
I encounter problems with the Exam(&Exam::cheat) thing. What I
understand so far is that Exam is taking the address of it's own cheat
variable. When entering the Exam's constructor cheat is uninitialized.
So for me I would initialize it here with false.
Static variables are initialized before the creating of the first instance of a class. The problem you have is two-fold: 1) you haven't initialized your static member variable, 2) you aren't treating it as a static member variable.
Declaring a variable as static means it will be shared between all instances of the class (e.g. there will be 1 memory location for cheat). Unless you want everyone taking the Exam to cheat as soon as 1 person cheats, this is not likely what you want.
To fix it, you would want your Exam class to look more like this:
class Exam
{
public:
Exam(bool cheat) : m_cheat(cheat) {}
// don't expose member variables directly - use accessor functions
void setCheating(bool cheat) { m_cheat = cheat; }
bool isCheating() const { return m_cheat; }
void start(int);
private:
bool m_cheat;
};
The corresponding changes to your main function
int main(void)
{
Exam e(false);
e.start(3);
e.setCheating(true);
if (e.isCheating())
e.start(4);
return 0;
}

Related

How can I compare the a variable in different instances of the same class?

I need to compare the variable that is stored in two different instances of the same class and it must return a bool.
I am trying to compare the ages of pepper and salty but not sure how to go about it.
This is the first time I post so if its not clear let me now. I would like to learn how to use this page for help and learning.
someone mentioned using the 'this' pointer not sure how that would work.
class dog
{
dog();
dog(int yearsOld);
bool sameAge(const dog& compareAge);
private:
int yearsOld;
}
dog pepper(7);
dog salty(9);
bool dog::sameAge(const dog& compareAge)
{
if(yearsOld == compareAge.yearsOld)
{
return true;
}
return false;
}
sameAge(salty)
Your comparison code is fine. The problem is that you're not calling the function correctly. You need to call the member function through an object. Otherwise, how will it know that it should be comparing with pepper?
if (pepper.sameAge(salty)) {
// do something
}
This is basic to all object-oriented programming -- methods are called through objects.

C++ can't access field from inherited class

Hello guys a have a problem, that i can't access field tablica[i]->help, in generuj function, its saying that this field is not existing in class Task.
How can i achieve it ?
class Task
{
protected:
string contents;
int id_pyt;
int nr_pyt;
};
class Task4Answ : public Task
{
private:
int help;
public:
Task4Answ(string contents1, int id,int nr,int help1)
{
contents=contents1;
id_pyt=id;
nr_pyt=nr;
help=help1;
}
};
class TaskCollection
{
protected:
Task *collection[60];
public:
friend class Generator;
TaskCollection()
{
collection[0] = new Task4Answ("Ile jest por roku w Polsce? \na) 1 \nb) 2 \nc) 3 \nd) 4",1,0);
collection[1] = new Task4Answ("Kto wygral tegoroczny Roland Garros? \na) Federer \nb) Djokovic \nc) Nadal \nd) Thiem",1,1);
class Generator
{
protected:
Task *tablica[10];
TaskCollection T1;
public:
Generator(){}
void Generuj()
{
if(T1.collection[x]->id_pyt==1)
{
tablica[i]=new Task4Answ("0",0,0);
tablica[i]->contents=T1.collection[x]->contents;
tablica[i]->id_pyt=T1.collection[x]->id_pyt;
tablica[i]->nr_pyt=T1.collection[x]->nr_pyt;
tablica[i]->help=T1.collection[x]->help; //here is the problem
}
}
}
Or maybe there is some other solution of the project im doing now.
Thanks for any help.
The problem is in this line:
tablica[i]=new Task4Answ("0",0,0);
Although you have called the Task4Answ constructor, you are also assigning the memory address returned by new to a Task pointer. Effectively, you have casted the Task4Answ pointer to a Task pointer. On the lines that follow, C++ only sees tablica[i] as a reference to a Task pointer. You need to change:
protected:
Task *tablica[10];
TaskCollection T1;
...to this:
protected:
Task4Answ *tablica[10]; // Task was changed to Task4Answ
TaskCollection T1;
That should allow C++ to see tablica as an array of Task4Answ pointers instead of Task pointers.
Edit: it looks like help is also private. You will have to change help to public or add TaskCollection::TaskCollection() as a friend. Otherwise, C++ will not let you get or set help.
Edit: the OP added that tablica[i] might contain instances of other classes that inherit from Task. In that case, you could do something like this:
void Generuj()
{
if(T1.collection[x]->id_pyt==1)
{
Task4Answ* newTask = new Task4Answ("0",0,0);
newTask->contents=T1.collection[x]->contents;
newTask->id_pyt=T1.collection[x]->id_pyt;
newTask->nr_pyt=T1.collection[x]->nr_pyt;
newTask->help=T1.collection[x]->help; // You will still have to change this from being private.
tablica[i] = newTask;
}
}
}
Later on, in order to access help, you will need to implement some sort of way of checking whether tablica[i] is a Task4Answ and not an instance of some other class that inherits from Task, perhaps by implementing a method in Task named IsTask4Answ that returns false in Task but is overridden to return True in Task4Answ. You can then cast the pointer back to Task4Answ with something like the static_cast operator. In other words:
// Add these functions to the class definitions:
virtual bool Task::IsTask4Answ() const {
return false;
}
bool Task4Answ::IsTask4Answ() const override {
return true;
}
// Later, you can do this:
if(tablica[i].IsTask4Answ()){
Task4Answ* t = static_cast<Task4Answ*>(tablica[i]);
t->help; // Again, you'll have to change this from being private.
}
Although I suggest figuring out a different data structure where you do not need to do any casting, this will allow you to access help.
Do note the virtual keyword in the first function above; it allows the function to be dynamically bound, which means that the code will check whether to call Task::IsTask4Answ() or Task4Answ::IsTask4Answ() at runtime instead of at compile time.

Cannot assign to non-static data member within const member function

I am attempting to use std::unordered_set as a hash table to store many CreditCard's. CreditCard and another class CardDatabase are defined as follows:
class CreditCard {
private:
string cardHolder;
unsigned long long cardNumber;
int limit;
int balance;
public:
CreditCard(string in_cardHolder, string in_cardNumber, int in_limit) {
cardHolder = in_cardHolder;
cardNumber = stoll(in_cardNumber);
limit = in_limit;
balance = 0;
}
void ChangeBalance(int amount) const {
balance += amount; // SECOND ERROR
}
};
class CardDatabase {
private:
unordered_set<CreditCard> cards;
unordered_set<CreditCard>::iterator iter;
public:
CardDatabase() { }
void AddCard(cardHolder, cardNumber, int limit) {
CreditCard tempCard = CreditCard(cardHolder, cardNumber, limit);
cards.insert(tempCard);
}
void Charge(string cardHolder, int chargeAmount) {
iter = cards.find(cardHolder);
iter->ChangeBalance(chargeAmount); // FIRST ERROR
}
}
Initially I was getting the following compile error at FIRST ERROR: Member function 'ChangeBalance' not viable: 'this' argument has type 'const CreditCard', but function is not marked const. So, I added the "const" to the ChangeBalance function. However, after doing that I get the following compile error at SECOND ERROR: Cannot assign to non-static member within const member function 'ChangeBalance'.
Is there any way to fix this error without changing balance to a static variable? It is obviously important that the balance be different for each CreditCard instance.
Any help is appreciated.
EDIT:
Thank you all for your quick answers. I feel I should clarify something. I already added the proper hash functionality elsewhere in my code:
namespace std {
template <>
struct hash<CreditCard> {
size_t operator()(const CreditCard& cc) const
{
return hash<string>()(cc.GetCardHolder());
}
}
}
Also, the code I posted initially pasted is from a much larger code base and I didn't delete all the necessary namespacing stuff at first before posting the question. My apologies for the confusion.
Members of an unordered_set are constant, and cannot be changed once they're in the unordered_set, by default. You are trying to change the objects in the set, and the compiler is properly telling you that you can't do this.
The only possible way to do this correctly (explained only for educational purposes, because this is bad class design):
Explicitly declare the individual fields that can be modified in this manner as mutable.
Use a custom hash function with your unordered_set, and the hash function must exclude the value of mutable fields from the value of the calculated hash.
Otherwise, modifying the contents of the object in the set obviously changes its hash value, which will result in undefined behavior.
Again, this is explained for informational purposes only. This is not a good class design.
The clean way to do this would be to assign a unique identifier to each CreditCard (you know, like a credit card number?), and use an ordinary std::map, to look up CreditCards by their number.
It's not appropriate for ChangeBalance to have const semantics. By the very nature of it's name, you're modifying the object. Make the function non-const.
void ChangeBalance(int amount) {
balance += amount;
}
The other problem is that you didn't call your function correctly. You should instead do this:
iter->ChangeBalance(chargeAmount);
I will mention there are cases where you want to modify values in a const object, and there is a mutable type modifier for that. Do not use it to solve your current error, however!
void ChangeBalance(int amount) should not be const - it is changing the object.
The problem is before in the iterator: cards.find returns a const object, so you are not allowed to change it.
A way to resolve that is to make your cards set a set of pointers to cards, not of cards; or to use another way to find the matching card
Playing fast and loose with the C++ syntax in that thar code, Hoss. Plenty of errors wait around the corner
First Error:
iter->CreditCard::ChangeBalance(chargeAmount);
should be
iter->ChangeBalance(chargeAmount);
Straight-up bad syntax that likely results from flailing around because of the errors resulting from unordered_set having no idea how to hash a CreditCard. Give this a read: How do I use unordered_set? That said, unordered_set is probably not the right solution for this job. std::map<std::string, CreditCard> looks more on point.
Using the wrong solution to fix the above problem lead to the
Second Error:
void ChangeBalance(int amount) const
const on a method means the method cannot change the state of the object. in ChangeBalance balance += amount; attempts to change the state of the object by updating a member variable.
In addition, the compiler is going to HATE the CreditCard:: in this:
CreditCard::CreditCard(string in_cardHolder, string in_cardNumber, int in_limit) {
cardHolder = in_cardHolder;
cardNumber = stoll(in_cardNumber);
limit = in_limit;
balance = 0;
}
Another solution is to make the "balance" as a static member.
class CreditCard {
private:
string cardHolder;
unsigned long long cardNumber;
int limit;
static int balance;
....
}
And then initialize it in cpp file
int CreditCard::balance = 0;
This code may not be very secure. But this can be one of the workaround.

find out identifer name and typeid of the object or variable in C++?

I've started learnig C++ (year ago) mostly because it's univerzal language IMO, and beacause almost everything is possible.
but one thing isn't so:
for example we are writing some code inside an object(class) and we need to find out it's name somehow:
class Test
{
public: const char* getMyIdentiferName()
{
// what now??
}
};
well the best option is to use 'this' keywod but that wouldn't help cos 'this' cant return name?
Test thatsMyName;
const char* = thtsMyName.getMyIdentiferName(); //return string "thatsMyName" how?
how do we get 'thatsMyName' string in in some generic function or even template??
ANOTHER EXAMPLE:(please answer this too)
how do we get typeid of some class?
class MyType
{
public: type_info getType()
{
return typeid(this); //that wont work of course :)
{
};
this looks funny but if any of you have some idea on how to achive similar task...
thanks alot.
EDIT: OK, everybodey say it's impossible to get the name of an object, I found out how to get the name:
class Test
{
public: string getObjectName()
{
string arg = typeid(*this).name();
arg.erase(arg.begin(), arg.begin() + 5);
arg.erase(0,1);
return arg;
}
};
int main()
{
Test thisIsMyName;
cout << thisIsMyName.getObjectName() << endl;
cin.ignore();
return 0;
}
EDIT:
Big thanks to Fiktik answering my second example who found the way oon how to get the type_info of the object!
The first thing you are asking is not possible. At least not directly.
You could create a macro for variable declaration that would register its name somewhere, something like this:
#define CREATE_VARIABLE(type, name) registerVariable<type>(#name); type name
but this is quite cumbersome and cannot be used everywhere. Why would you even want to have this functionality?
The second thing should work with only little adjustments:
class MyType
{
public:
const type_info& getType()
{
return typeid(*this);
}
};
What would you need the variable name for? What you're trying to do is impossible; inside an classes' methods, the name of the variable used for accessing an object isn't known.
Consider this example:
Test * myTest = new Test();
Test * myTest2 = myTest;
const char* identifier = myTest2->getMyIdentifierName();
What should getMyIdentifierName() return? "myTest"? "myTest2"? Nothing at all, since the object was allocated dynamically, and therefore no variable can really claim to "hold" the object?
If you just want to know the variable name which you are currently using for referring to an object, why not just use a string literal for it and pass it wherever you need it? But that doesn't seem to make sense in any real-world application.
If you need a name (i.e., a unique identifier) for an object at runtime, you'll best give the class a member name and initialize that with whatever you need.
Getting the "identifier name" of an object won't work, since there is no unique identifier for an object (other than it's address, to be gotten with &). An unlimited number of references/pointers can designate the same object; at the same time, the language allows objects to be reached by other means than identifiers (v[0] where v is a vector is an object, but not an identifier). This is the case in all programming languages that I know, and I know a dozen of them.
As for your second question, you can return a reference to an std::type_info.
struct Test
{
// pretty useless method
std::type_info const &type() const { return typeid(*this); }
};
int main()
{
Test a;
std::cout << a.type().name() << "\n";
}

How can I make an instant-dependant static variabe in a class method?

I have a function in a class the more or less works like so:
class Player {
private:
Object* minions[16]
public:
void Summon(Object* obj);
};
Player::Summon(Object* obj) {
static int i = 0;
if (i == 16)
return;
minions[i] = obj;
i++;
}
The problem arise when trying to use more than one player, like so:
Player playerone;
Player playerthree;
playerone.Summon(new Object("o1"));
playerthree.Summon(new Object("o2"));
o1 is located in playerone.minions[0], as is expected, however, o2 is located in playerthree.minions[1], the Summon() function using the same i variable. Is there a way to make the Summon() function use a static i variable for a single instance, but use separate i variables for each instance? I know I could do something like make a for loop to the first spot in minions[] equal to NULL, or make i a member of Player directly, but I want to know if there is a better way before I do either of those.
Change Object* minions[16]; to a std::vector<Object*> minions;. That way you can just use minions.size() to know how many there are, or minions.push_back(obj); to add one without worrying about array index stuff.
Why don't you simply put i in each Player? I'd rename it something like summonned_minion_count, but that's the actual intent of what you want to do.
Making a local variable static is effectively making it global. You should simply make i a data member of class Player. And probably give it a more descriptive name.
You need to make your i a member variable of Player.
Or even better you could do something like this:
#include <vector>
class Player {
private:
static int const MAX_MINION_COUNT = 16;
std::vector<Object *> minions;
public:
void Summon(Object* obj) {
if (minions.size() < MAX_MINION_COUNT) {
minions.push_back(obj);
}
}
};