Sign in to follow this  
swilkewitz

Passing "this" pointer to member object

Recommended Posts

I'm currently revamping the structure for my game, and I need to pass the address of an object to one of its members. I tried to use the "this" pointer, but that didn't work. I was able to get around the problem by passing the pointer of the object to its own constructor and then passing that to the member. I'm guessing there is a better way to do this though.

Thanks!
Scott Wilkewitz

Share this post


Link to post
Share on other sites
1, Why do you want to pass the address?
2, Why passing "this" doesn't work? What error?

More details please.

Share this post


Link to post
Share on other sites
[quote name='wqking' timestamp='1306894377' post='4818126']
1, Why do you want to pass the address?
2, Why passing "this" doesn't work? What error?

More details please.
[/quote]

1, I've got a chain of classes that store each other, and I want to be able to access methods from higher-up classes.

2, Its a runtime error. Basically, when I use the pointer to access the methods nothing happens. Perhaps the problem is with parallel objects? All I know is that getting the pointer from outside the constructor works.

Share this post


Link to post
Share on other sites
I don't quite understand what exactly is your goal. As it sound like, you simply want something like this, but it's rather useless:

[code]class A
{
public:
A() : memberPointer(this) {}
~A() {};

private:
A* memberPointer;
};[/code]

Or maybe instead of a pointer as the member variable, you have another class B and you need to pass A pointer to B through the constructor or a method or whatever. Shouldn't be a problem nonetheless.

Share this post


Link to post
Share on other sites
It's quite possible to do this:
[code]class Member
{
public:
SetOwner(Owner *owner);
};

class Owner
{
public:
Owner() { member.SetOwner(this);}

Member member;
}[/code]

This is wrong:

[code]Owner owner;
owner.member.SetOwner(this);[/code]
But you can go like this:

[code]Owner owner;
owner.member.SetOwner(owner);[/code]

If you are wanting a [i]reference [/i](or just a regular variable) and not a [i]pointer[/i], you can go like this:

[code]class Member
{
public:
SetOwner(Owner &owner);
};

class Owner
{
public:
Owner() { member.SetOwner( *this ); } //De-reference the pointer.

Member member;
}[/code]

Share this post


Link to post
Share on other sites
If you pass the pointer during construction, you shouldn't call any methods on that pointer until the enclosing class has completed construction. Otherwise you'll be accessing partially constructed object. In particular, calling virtual methods when only part of the inheritance hierarchy has been constructed is a bad idea.

See if you can separate the objects to introduce a third object that both depend on:
[code]
class Shared
{
public:
Shared(int n) : n(n)
{
}

void foo()
{
std::cout << "Shared: " << n << '\n';
}

private:
int n;
};

class Member
{
public:
Member(Shared *shared)
{
shared->foo();
}
};

class Owner
{
public:
Owner()
:
shared(42),
member(&shared)
{
}

private:
Shared shared; // <-- Ordering here is vital!
Member member;
};

int main()
{
Owner owner;
}
[/code]
This is cleaner if you can manage it. If not, you probably should wait until the Owner is constructed before giving the Member a pointer to it. Unless you can guarantee you won't be doing anything dangerous with that pointer until the Owner is fully constructed.

Share this post


Link to post
Share on other sites
I am using the pointers to create a hierarchy of unrelated classes. The pointers would create a sort of "spine."

Game
>World
>Actor

A Game would hold an instance of World, and a World would hold a pointer of Game. So when Game is being constructed I want to create a World and set the World's pointer to the Game.

I thought that using the "this" pointer would work fine, but I wasn't able to get the "spine" working.



"This is wrong:"

[code][color="#660066"]Owner[/color] owner[color="#666600"];[/color]
owner[color="#666600"].[/color]member[color="#666600"].[/color][color="#660066"]SetOwner[/color][color="#666600"]([/color][color="#000088"]this[/color][color="#666600"]);[/color][/code]


"But you can go like this:"

[code][color="#660066"]Owner[/color] owner[color="#666600"];[/color]
owner[color="#666600"].[/color]member[color="#666600"].[/color][color="#660066"]SetOwner[/color][color="#666600"]([/color]owner[color="#666600"]);[/code]

[/color]

I know the first one is wrong, I never tried to do that. The second way is how I've been getting around the problem... though I've been passing &owner instead of owner.


Share this post


Link to post
Share on other sites
[quote name='swilkewitz' timestamp='1306940984' post='4818293']
I am using the pointers to create a hierarchy of unrelated classes. The pointers would create a sort of "spine."

Game
>World
>Actor

A Game would hold an instance of World, and a World would hold a pointer of Game. So when Game is being constructed I want to create a World and set the World's pointer to the Game.

I thought that using the "this" pointer would work fine, but I wasn't able to get the "spine" working.
[i]... gd.net reformatted puke elided ...[/i]

I know the first one is wrong, I never tried to do that. The second way is how I've been getting around the problem... though I've been passing &owner instead of owner.
[/quote]
So, what's the problem? That's fairly idiomatic.
[code]
World::World()
: game(this)
{ }

Game::Game(World* world)
: actor(world)
{ }
[/code]

Share this post


Link to post
Share on other sites
[quote name='Bregma' timestamp='1306947752' post='4818330']
[quote name='swilkewitz' timestamp='1306940984' post='4818293']
I am using the pointers to create a hierarchy of unrelated classes. The pointers would create a sort of "spine."

Game
>World
>Actor

A Game would hold an instance of World, and a World would hold a pointer of Game. So when Game is being constructed I want to create a World and set the World's pointer to the Game.

I thought that using the "this" pointer would work fine, but I wasn't able to get the "spine" working.
[i]... gd.net reformatted puke elided ...[/i]

I know the first one is wrong, I never tried to do that. The second way is how I've been getting around the problem... though I've been passing &owner instead of owner.
[/quote]
So, what's the problem? That's fairly idiomatic.
[code]
World::World()
: game(this)
{ }

Game::Game(World* world)
: actor(world)
{ }
[/code]
[/quote]

That is what I tried to do originally, but when I called higher-up functions using the pointers nothing happened. By using the workaround the pointers work, but I'd rather not use the workaround because that doesn't actually solve the problem.

Also, I'm getting memory problems now. I've been experimenting a lot....
When I start my game in debug mode everything is fine, but when I switch to release mode eveything goes haywire. I looked at taskmanager while running in release and I noticed the used memory cycling between 1.5 GB and 2.5 GB before crashing.

Unhandled exception at 0x768bb727 in Triax Bridge Command.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0035ed40..

I'm guessing I'm not being very careful with my pointers.

Share this post


Link to post
Share on other sites
[quote name='swilkewitz' timestamp='1306948961' post='4818348']
Unhandled exception at 0x768bb727 in Triax Bridge Command.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0035ed40..

I'm guessing I'm not being very careful with my pointers.
[/quote]
It appears that some state of yours is either really messed up (possibly due to scribbled-on memory), or you've accidentally tried to allocate a ridiculous amount of memory.

What does your debugger tell you?

Share this post


Link to post
Share on other sites
[quote name='edd²' timestamp='1306951431' post='4818371']
[quote name='swilkewitz' timestamp='1306948961' post='4818348']
Unhandled exception at 0x768bb727 in Triax Bridge Command.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0035ed40..

I'm guessing I'm not being very careful with my pointers.
[/quote]
It appears that some state of yours is either really messed up (possibly due to scribbled-on memory), or you've accidentally tried to allocate a ridiculous amount of memory.

What does your debugger tell you?
[/quote]


I tracked through the creation of my objects and found these to be requiring too much memory:

[code]Ship* s = new Ship();
s->addTexture(fileManager.loadTexture("ship1.bmp"));
s->addModel(fileManager.loadModel(s->scale, "ship1.model"));
space.addActor(s);

Asteroid* a = new Asteroid();
a->addTexture(fileManager.loadTexture("asteroid.bmp"));
a->addModel(fileManager.loadModel(a->scale, "asteroid.model"));
space.addActor(a);[/code]

The problem only comes up if I use both addTexture() and addModel() for only one of the objects.



Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1306959751' post='4818416']
Are you loading unique models/textures for each object, or are you cachine and sharing them somewhere?
[/quote]

So far they are unique (There are only two objects being loaded right now).

This solved the problem:

[code]Ship* s = new Ship();
GLuint tempTex = fileManager.loadTexture("ship1.bmp");
s->addTexture(tempTex);

Model* tempModel = new Model(fileManager.loadModel(s->scale, "ship1.model"));
s->addModel(*tempModel);
space.addActor(s);

delete tempModel;[/code]

Should I stick with that solution or is that just dodging a worse problem?




Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1306966514' post='4818440']
What does fileManager.loadModel return? Does the Model class obey the "Rule of Three"?

You are probably just masking the problem, yes.
[/quote]

I have heard of the rule of three before but never needed it. Now that I have read the wiki article about the rule I realize this is most likely the problem.

Also, I've been reading this faq
http://www.parashift.com/c++-faq-lite/
and I read something about handles being "ambiguous pointers." When it comes to the structure of my game, should I create a handle class so that I can change my method of object acquisition later on? I'm not sure I want to use pointers anymore because that faq recommends avoiding them, and they have been a pain so far.

What would you recommend for chaining the layers of my hierarchy?

Share this post


Link to post
Share on other sites
[quote name='swilkewitz' timestamp='1306976398' post='4818482']
When it comes to the structure of my game, should I create a handle class so that I can change my method of object acquisition later on?
[/quote]
IMO, strive to be simple and direct until you definitely know you'll need something a little fancier. Over-engineering is an all-too-common affliction.

[quote]
I'm not sure I want to use pointers anymore because that faq recommends avoiding them, and they have been a pain so far.
[/quote]
I tend to avoid naked/raw pointers for anything except algorithmic or low-level work (e.g. image processing, a custom container, etc). For resource management, I'll always opt to create a smart pointer whose type describes the ownership semantics. Examples include boost::scoped_ptr and shared_ptr. If somebody hands you a shared_ptr, that says much more than being handed a raw pointer, for example.

If you have an aversion to boost, many modern C++ implementations come with similar classes as part of their standard library, in an std::tr1:: or stdext:: namespace or something similar. It's also a fun exercise to try to make such smart pointers for yourself, but it might be an idea to get them reviewed before relying on them too heavily :)

Share this post


Link to post
Share on other sites
Ok, I replaced the plain pointers with auto pointers, though I don't know for sure if I am using them right. I'm pretty sure I am:

[code]Ship* ship = new Ship();
auto_ptr<Actor> s(ship);
s->addTexture(fileManager.loadTexture("ship1.bmp"));
s->addModel(Model(fileManager.loadModel(s->scale, "ship1.model")));
space.addActor(s); //Space is a World[/code]

[code]class World
{
public:
auto_ptr<Game> rootGame;
vector<auto_ptr<Actor>> actors;
}

World::World(auto_ptr<Game> g)
{
rootGame = g;
}

void World::addActor(auto_ptr<Actor> a)
{

a->rootWorld = this; //I haven't added the auto pointers everywhere yet, so Actor still uses the plain pointers
actors.push_back(a);
}

auto_ptr<Game> World::getGame()
{
return rootGame;
}

void World::act(GLfloat dt)
{

for(int i = 0; i < (int) actors.size(); i++){
actors[i]->act(dt);
}

//for each(auto_ptr<Actor> a in actors){ //Is there a way to use for each loops with auto pointers?
//a->act(dt);
//}
}[/code]

The only problem I'm getting is that there is "[font="Consolas"][size="1"][font="Consolas"][size="1"]no copy constructor available or copy constructor is declared 'explicit'[/size][/font][/size][/font]" I'm guessing that is a prerequisite for using auto pointers... I'll work on adding that, and if I'm using auto pointers wrong please tell me.

Thank you all so much for all the help!
Scott

Share this post


Link to post
Share on other sites
auto_ptr<>s have somewhat strange copy semantics. They're not "copyable enough" to be put in to containers such as std::vector, list and so on. So even if you do manage to get your code to compile, it likely won't be correct.

Something along the lines of boost::shared_ptr/std::shared_ptr/std::tr1::shared_ptr/stdext::shared_ptr is probably what you'd want, there.

Share this post


Link to post
Share on other sites
I gave in and decided to use boost, so I installed it and replaced the auto pointers with boost's shared pointers. I've been doing some research and found out that to pass "this" you have to "[font="Consolas"][size="2"][font="Consolas"][size="2"]enable_shared_from_this<T>" [font="Arial"]and then use "[/font][size="2"][font="Consolas"][size="2"][font="Arial"]shared_from_this()." So that got rid of one problem I was getting, but now I have another. I think it might be a pointer cycle bug, but I'm not sure. I do know that in that case I would have to use a weak pointer somewhere. Heres my code:
[/font][font="Consolas"]
[code]class Game: public enable_shared_from_this<Game>
{

public:
World space;
Game();
}

Game::Game()
{

space = World(shared_from_this());

shared_ptr<Actor> s(new Ship());
s->addTexture(fileManager.loadTexture("ship1.bmp"));
s->addModel(Model(fileManager.loadModel(s->scale, "ship1.model")));
space.addActor(s);
}[/code]

[code]class World: public enable_shared_from_this<World>
{

public:
shared_ptr<Game> rootGame;
vector<shared_ptr<Actor>> actors;

World(shared_ptr<Game> g);
void addActor(shared_ptr<Actor> a);
shared_ptr<Game> getGame();
}

World::World(shared_ptr<Game> g)
{
rootGame = g;
}

void World::addActor(shared_ptr<Actor> a)
{

a->rootWorld = shared_from_this();
actors.push_back(a);
}

shared_ptr<Game> World::getGame()
{
return rootGame; //Should I return a raw pointer here? this will only be used for accessing methods
//in the higher-up classes...
}[/code]




[/font][/size][/font][/size][/size][/font][/size][/font]

Share this post


Link to post
Share on other sites
I ran it through the debugger and found that the first use of shared_from_this(); created this error:

Unhandled exception at 0x768bb727 in Triax Bridge Command.exe: Microsoft C++ exception: std::tr1::bad_weak_ptr at memory location 0x0027f780..

[code]Game::Game()
{

space = World(shared_from_this());
}[/code]

Share this post


Link to post
Share on other sites
shared_from_this is [url="http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/enable_shared_from_this.html"]documented[/url] as follows:
[quote]
Requires: enable_shared_from_this<T> must be an accessible base class of T. *this must be a subobject of an instance t of type T . [b]There must exist at least one shared_ptr instance p that owns t.[/b]
[/quote]
In your game constructor this isn't true.

I still think you are introducing an unnecessary cycle here. Why does the World need to know about the game? Can any logic that needs to be shared be pushed into a separate class or delegate?

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1307087923' post='4818965']
shared_from_this is [url="http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/enable_shared_from_this.html"]documented[/url] as follows:
[quote]
Requires: enable_shared_from_this<T> must be an accessible base class of T. *this must be a subobject of an instance t of type T . [b]There must exist at least one shared_ptr instance p that owns t.[/b]
[/quote]
In your game constructor this isn't true.

I still think you are introducing an unnecessary cycle here. Why does the World need to know about the game? Can any logic that needs to be shared be pushed into a separate class or delegate?
[/quote]

Well, I was just trying to structure my game differently. I don't want to have to hard code the passing of all my game tools every time I add a new one, so I figured I could just link the lower classes to the actual game class. Maybe I should just go back to the drawing board.

Share this post


Link to post
Share on other sites
[quote name='swilkewitz' timestamp='1307072838' post='4818935']
I ran it through the debugger and found that the first use of shared_from_this(); created this error:

Unhandled exception at 0x768bb727 in Triax Bridge Command.exe: Microsoft C++ exception: std::tr1::bad_weak_ptr at memory location 0x0027f780..

[code]Game::Game()
{

space = World(shared_from_this());
}[/code]
[/quote]
Did you derive [font="Courier New"]Game[/font] publicly from [font="Courier New"]std::enable_shared_from_this<Game>[/font]?

Share this post


Link to post
Share on other sites
You can't call shared_from_this() in the constructor of the class that derives from enable_shared_from_this<Me>.

[s]It's an undocumented limitation :/[/s] EDIT: rip-off said this better.

In addition, if you really do want to inherit enable_shared_from_this, I'd recommend using the named constructor idiom in combination with private constructors to ensure that all instances are create in a shared_ptr (presumably this is what you're going for here?)

But creating reference cycles as you're doing here is very fishy.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this