Passing class by reference doesn't change original - c++

I have a class that handles rendering sprites, which has a Ball and Gun objects on it, sets their sprites, and renders them every frame. I want to pass in that ball object to gun, so that gun can fire when a user presses space (input is handled on gun). I tried pointers, and now passing by reference, but when I change it after passing it in it doesn't affect the original. In my main class I call gun.init(&ball) that I'll show here. Gun has a variable Ball mBall.
class Gun : public GameObj
{
public:
Gun()
:GameObj(), mWaitSecs(0)
{}
void Update();
void Init(Ball &ball); //init function that passes in ball?
private:
float mWaitSecs; //delay after firing beforeo you can move/fire
Ball mBall;
};
void Gun::Init(Ball &ball) {
mBall = ball;
}

[..] but when I change it after passing it in it doesn't affect the original.
Frankly, you have a basic misunderstanding. And to clear that up, I have to take a small detour.
Pass by value
void foo_value(int x) { x += 42; }
calling this function via
int y = 50;
foo_value(y);
will not change the value of y.
Pass by reference
void foo_ref(int& x) { x += 42; }
calling this function will modify y:
int y = 50;
foo_ref(y);
assert(y == 50+42);
Rebinding references
You cannot rebind references. That's why you cannot not initialize a reference:
int& y; // error
int x;
int& y = x; // ok
Once you have a reference you cannot make it refer to something else. You cannot "reassign" the reference:
int x = 0;
int& ref = x;
int y = 42;
ref = y; // same as x = y;
after the last line, ref is still a reference to x.
Your code
You do pass ball by reference, but your member mBall is not a reference. mBall is a copy of the ball refered to by ball. An example similar to your code is
void foo_copy_from_ref(int& x) {
int mx = x;
mx = 42;
}
Modifying a copy has no effect on the original, no matter if you made the copy from a reference.
You can have a reference to a Ball as member, but you have to initialize it in the constructor and you cannot make it refer to a different Ball later.

Related

When does a variable copy be released in lambda function?

As the example below, I define 2 variables x and y. When I call the lambda function twice, it seems like it will not destroy the copy. From 11.14 — Lambda captures | Learn C++ - Learn C++, it says:
Because captured variables are members of the lambda object, their values are persisted across multiple calls to the lambda!
How does C++ manage the memory for lambda function?
int main() {
int x = 1;
static int y = 1;
auto fun = [=]() mutable{
x++;
y++;
cout<<"Inside:\t\t";
cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
};
for (int i = 0; i<2; i++) {
fun();
cout<<"Outside:\t";
cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl<<endl;
}
}
Output:
Inside: x:2 y:2
Outside: x:1 y:2
Inside: x:3 y:3
Outside: x:1 y:3
It stores it inside the object itself. Another way to think of your lambda is below. This is "kind of" equivalent to what the compiler is generating, and yes, I'm changing scopes around a bit and I know that, but this may be clearer to a beginner at C++.
static int y = 1; // Moved this out from main
class my_lambda{
public:
my_lambda(int x) : _x(x) {}
~my_lambda() = default;
void operator()()
{
_x++;
y++;
cout<<"Inside:\t\t";
cout<<"_x:"<<_x<<"\t"<<"y:"<<y<<endl;
}
private:
int _x;
};
int main() {
int x = 1;
my_lambda fun{x}; // "Captures" x
for (int i = 0; i<2; i++) {
fun();
cout<<"Outside:\t";
cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl<<endl;
}
}
As you can hopefully see, the "fake lambda" class I made is doing the same thing your actual lambda is. It "captures" x as part of its constructor and copies it to internal storage, which I called _x. And y is just in scope for it, though I moved to global to be above the class declaration.
I'm overloading the () operator to make a class that's callable, which is a normal thing to do in some circumstances. See operator overloading if you wish for more information.
So this is "kind of, sorta" how lambdas work. They take your body and put it into the operator() of the object that's generated, and anything captured is one of the object's variables, with either the actual object (if by value) or a reference, if captured that way.
And to directly answer your question: when fun goes out of scope, it is freed, both in a lambda, and my case.

Assigning value to returned shared_ptr doesn't behave as expected

I have a private three dimensional vector of shared_ptr<Room> objects as follows:
private:
vector<vector<vector<shared_ptr<Room>>>> world;
In the same class, I provide access to Room objects:
public:
shared_ptr<Room> room_at(const int & x, const int & y, const int & z) const
{
return world.at(x).at(y).at(z);
}
Also in the same class, I initialize the world structure:
for (int x = 0; x < C::WORLD_X_DIMENSION; ++x)
{
vector<vector<shared_ptr<Room>>> row;
for (int y = 0; y < C::WORLD_Y_DIMENSION; ++y)
{
vector<shared_ptr<Room>> vertical_stack; // "stack"
for (int z = 0; z < C::WORLD_Z_DIMENSION; ++z)
{
vertical_stack.push_back(shared_ptr<Room>(nullptr));
}
row.push_back(vertical_stack);
}
world.push_back(row);
}
Later, I want to save a Room object into world:
void add_room_to_world(const int & x, const int & y, const int & z)
{
shared_ptr<Room> room = make_shared<Room>(); // create an empty room
/* (populate room's member fields) */
// add room to world
room_at(x, y, z) = room; // This doesn't work as expected
}
The shared_ptr within world starts out as nullptr as expected, but doesn't change on the last line above.
Based on what I've found on SO, I've tried operator= (above), .reset(room), and make_shared<Room>(room) (using an actual Room object rather than shared_ptr<Room>) but in all cases, the shared_ptr within world stays set to nullptr.
What is the correct way to assign objects into world?
room_at returns a value. When it is returned from the function it is copied, so any operations that you do on the returned value don't affect the original value. If you want to change the original value you have to return a reference like this:
shared_ptr<Room>& room_at(const int & x, const int & y, const int & z) const
{
return world.at(x).at(y).at(z);
}
If you don't want users of your class to be able to do this, declare this method private and keep the original as it is.
room_at returns a value. When you mutate this value, who cares? You're just mutating some random temporary. It has no meaning to the object it was copied from.
To support what you want, room_at would need to return a mutable reference- a very bad idea for a public API in this fashion.
You would want to offer an analagous private method that returns the mutable reference, and then implement room_at as just returning a copy of the object referenced by that function.

Can a "public:" function built into a class alter the class object it was called from?

apologies in advance, I feel like i'm missing something major here. Any time i call a function this way it fails to alter the objects member variable.
The easiest way for me to explain is to give an example:
class herp
{
private:
string derp;
public:
void mderp(herp x, string y) {x.derp = y;}
}
herp object_0;
string temp = "asdf";
object_0.mderp(object0, temp);
You are passing herp x as a value type. In this case x is a copy of whatever you pass in. If you want to directly access x.derp, you should pass x in as a reference:
void mderp(herp& x, string y) {x.derp = y;}
This should allow x.derp to be modified.
The argument herp to is passed by value, that means it is a copy of the original object_0. The mderp function modifies the copy, but that copy is thrown away immediately. Try a reference. I might do:
class herp
{
private:
string derp;
public:
static void mderp(herp &x, string y) {x.derp = y;}
}
herp object_0;
string temp = "asdf";
object_0.mderp(object0, temp);
Here the important thing is the change herp x -> herp& x which means the argument is "a reference to herp" rather than an actual herp value.
In this case I also made the method static, because it can be but that is not essential.
replace
x.derp = y;
with
this->derp = y;
What I get from your question, is that you want to initialize the private variable of your class. But there are a couple of things wrong here. You should read more about classes.
One way:
class herp
{
private:
string derp;
public:
void mderp(string y) {derp = y;}
};
herp object_0;
string temp = "asdf";
object_0.mderp(temp);
There are two things you should probably know.
First of all your object's member variable does not get changed because the way you wrote your code, your function mdepr creates a copy of the object herp. It then changes its member variable and destroys the object upon leaving the function. So if you want to keep your function the way it is you should use a reference or a pointer. Example:
void mderp(herp& x, string y) {x.derp = y;}
void mderp(herp* x, string y) {x->derp = y;}
The second thing is, based on your example usage of the function, you probably don't want a function like that. Member functions are meant to be used on a object and are allowed to modify it (unless they are const functions). So you can just write the following:
class herp
{
private:
string derp;
public:
void mderp(string y) {derp = y;}
}
herp object_0;
string temp = "asdf";
object_0.mderp(temp);
Hope this helps :)

dynamic cast fails on downcasting from parent to child

I have the following parent child simple classes:
class BoundBases{
public:
virtual ~BoundBases() { }
};
// Rectangular Bounds for tree
class RectBounds : public BoundBases{
public:
// x, y center point
double x, y;
double w, h;
~RectBounds(){ }
// (_x, _y): center of rectangle bound. (_w, _h): width and height
RectBounds(double _x, double _y, double _w, double _h){
x = _x;
y = _y;
w = _w;
h = _h;
}
//... more functions
};
I also have the following function structure:
void MyClass::init( BoundBases &bounds, std::vector<int> &colsPartitioned)
{
printf("init - new\n");
BoundBases * bPtr = &bounds;
RectBounds * rBounds = dynamic_cast<RectBounds *>(bPtr);
if(rBounds){
// do something
}else{
throw runtime_error("dynamic cast fail");
}
}
The dynamic cast is failing even though I call the function with RectBounds type as an argument. What is the reason?
FIXED:
The function calling init passed BoundBases by value, as follows:
MyClass2::MyClass2( BoundBases boundBases, std::vector<int> colsPartitioned) { // creates new table
// set up partition
partScheme_ -> setColsPartitioned(colsPartitioned);
partScheme_ -> setBoundBases(boundBases);
partScheme_ -> init(boundBases, colsPartitioned);
}
I changed the signature to pass by reference and it worked. (&boundBases). Can someone explain why is that? I am new to C/C++.
You need a reference here because dynamic_cast will only work if the real type of your variable is of type RectBounds like :
BoundBases* dummy = new Rectbound();
You can downcast here because the real type is Rectbound, so it will work.
If you pass it by value, it will create a copy of only the BoundBase part of your object, losing the information about your real type.
This problem is known as slicing
I'm not sure why you are surprised by that behavior. BoundBases passed by value is just a BoundBases. So dynamic_casting that to a child cannot make that a RectBounds. That's exactly what dynamic_cast is supposed to do.
If it worked differently: How would it be determining what e.g. x,y are if it's only given a BoundBases. This is not defined.

C++ Function call as a parameter, not working

http://pastebin.com/CsViwQFg
I'm using an SDK known as DragonFireSDK and there's a function called TouchAdd() that let's me add a function as a parameter (in this case: MoveLeft() and MoveRight()).
The only problem is, if the function is in a class (in this case, the Player class), I get the following errors:
Player *player;
void AppMain()
{
player = new Player(20,20,10);
tleft = TouchAdd(0,0,180,480,player->MoveLeft,0);
tright = TouchAdd(180,0,180,480,player->MoveRight,0);
}
The error:
error C3867: 'Player::MoveLeft': function call missing argument list; use '&Player::MoveLeft' to create a pointer to member
error C3867: 'Player::MoveRight': function call missing argument list; use '&Player::MoveRight' to create a pointer to member
If you want to pass function as a parameter then syntax is &Player::MoveLeft; as it is not bound to any object such as player.
The DragonFireSDK appears to want a "C" callable function and you're trying to pass a member function (though not using the right syntax). I think you'll need to do something like:
Player *player;
extern "C"
int PlayerMoveLeft(int id, int event, int x, int y)
{
// do something - I'm not sure what might be possible
// to get a pointer or a reference to the player object
// hopefully one or more parameters passed to this callback
// will have the information you need to do that
// or if you only have one global player, you're set -
// just use it
Player* player = /* ??? */;
player->MoveLeft( id, event, x, y); // or whatever needs to be passed
return 0;
}
extern "C"
int PlayerMoveRight(int id, int event, int x, int y)
{
Player* player = /* ??? */;
player->MoveRight( id, event, x, y); // or whatever needs to be passed
return 0;
}
void AppMain()
{
player = new Player(20,20,10);
tleft = TouchAdd(0,0,180,480,PlayerMoveLeft,0);
tright = TouchAdd(180,0,180,480,PlayerMoveRight,0);
}
Note that even though a static member function will often work (since there's no 'hidden' this pointer passed in, strictly speaking you should use non-member extern "C" functions.
Since the function signature of TouchAdd (taken from here) is
int TouchAdd(int x, int y, int width, int height, int (*callback)(int id, int event, int x, int y), int id);
the expected function must be a free function, eg:
int myCallback(int id, int event, int x, int y){
// do your stuff
}
void AppMain(){
tLeft = TouchAdd(....,&myCallback,...);
}
You can't pass a member function pointer (&Player::MoveX), since that function needs to be called on an object of that class (Player). So you need to use a work-around for that:
Player* player;
int PlayerMoveLeft(int id, int event, int x, int y){
return player->MoveLeft(id,event,x,y);
}
int PlayerMoveRight(int id, int event, int x, int y){
return player->MoveRight(id,event,x,y);
}
void AppMain(){
player = new Player(20,20,10);
tLeft = TouchAdd(...,&PlayerMoveLeft,...);
tRight = TouchAdd(...,&PlayerMoveRight,...);
}
}
It seems like id is the custom parameter that gets passed to the callback. If you only have 32-bit targets (and it seems like DragonFireSDK is meant only for iPhone, so I guess the answer is yes), you can cast it to Player* to bind to the player instance.
int PlayerMoveLeft(int id, int event, int x, int y)
{
Player* player = reinterpret_cast<Player*>(id);
return player->MoveLeft(event, x, y);
}
int PlayerMoveRight(int id, int event, int x, int y)
{
Player* player = (Player*)id;
return player->MoveRight(event, x, y);
}
void AppMain()
{
Player* player = new Player(20,20,10);
tleft = TouchAdd(0,0,180,480,PlayerMoveLeft,(int)player);
tright = TouchAdd(180,0,180,480,PlayerMoveRight,(int)player);
}
Even if that doesn't work, or you don't want to use kinda-ugly type casts, you can always have a global or static object with lookup tables. Making PlayerMoveLeft and PlayerMoveRight static members of the Player class may also look nicer, and I think it should play well with TouchAdd().
tleft = TouchAdd(0,0,180,480,player->MoveLeft,0);
tright = TouchAdd(180,0,180,480,player->MoveRight,0);
You're not passsing arguments to MoveLeft and MoveRight functions. I suppose they're function call as the title of your topic says, so you must pass arguments as well IF they take arguments.
If they're don't take argument, then do this:
tleft = TouchAdd(0,0,180,480,player->MoveLeft(),0);
tright = TouchAdd(180,0,180,480,player->MoveRight(),0);
If they're NOT function calls, instead you want to pass the member function pointers, then do this:
tleft = TouchAdd(0,0,180,480, &Player::MoveLeft,0);
tright = TouchAdd(180,0,180,480, &Player::MoveRight,0);
You also need to pass the instance so that member functions can be invoked later on.
It would be better if you let us know the signature of TouchAdd function. So that we can answer more specifically.