 |
 |
|
 |
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Hi everyone,
Months ago I updated my NearInfinity demo, but never got around to
posting it. It can be found here:
http://www.caltel.com/~abenge/NearInfinity.zip
It should look like this:
http://www.caltel.com/~abenge/NIscreen.jpg
There is no goal to the game. You just fly around in a near-infinite
landscape. I would call it infinite, but even the best random number
generators don't go on forever. The landscape is not different every
time you start the program up. You can change the landscape's seed
within an .ini file. To make it a little more interesting, there are six
Easter eggs to find. They occur very rarely, and you get no reward for
finding them; they are simply there. Look at ee.png for spoilers :) Keys
are left, right, up and/or z. It has bouncy and smooth, but solid
collision-detection.
If you are running Windows, have DirextX 8.0 or above, and have some
free time, could you download it and tell me what you think? With the
default settings it should run at 60 FPS regardless on your monitor's
refresh rate. I kept the logic separate from the frame rate, so it
should work on your computer exactly as it does mine, as long as your
computer isn't really old. It will skip frames to compensate if your
computer is slow.
I would like some feedback on a few things. What is your operating
system? Does it work on Vista? What FPS are you getting? Do the default
settings of 1280x960 fullscreen mode give you ultra-smooth scrolling?
What about lower resolutions? You can change these and other settings
within the NI.ini text file. If you decide to run in windowed mode,
change dynamicFrameRate to method 2. You won't get the benefits of
VSYNC, and it really shows.
If for some reason I didn't upload the program with the default 1280x960
resolution and VSYNC settings, change the values found in NI.ini to these:
windowed=0
screenMode=2
dynamicFrameRate=3
Thanks in advance!
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge wrote:
> it should run at 60 FPS regardless on your monitor's refresh rate.
Actually, it should run at whatever your monitor's refresh rate is and
it should do it smoothly with VSYNC, without affecting the game logic.
I just caught a few bugs and uploaded a new version (with a green
landscape). For some reason, I had forgotten to free a texture, which
isn't good! There is now a delete for every new, and all textures and
entities are freed up like they should be.
There is an issue that's bugging me though. Can anyone tell me why I get
a warning when I try to delete an array of sprite objects like so:
delete [] tile;
The compiler does not complain when I delete them like this:
v=0;
for(y=0;y<8;y++){
for(x=0;x<8;x++){
delete tile[int(v)];
v++;
}
}
I don't know why. I initialized the array like this:
hgeSprite *tile[8*8]
and filled it like this:
v=0;
for(y=0;y<8;y++){
for(x=0;x<8;x++){
tile[int(v)]=new hgeSprite(tiles,x*132+1,y*132+1,128,128);
v++;
}
}
v is a float, by the way. I'm not calling int() for no reason. Should I
have structured my "new" differently?
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
On Fri, 12 Dec 2008 22:38:26 +0100, stbenge
<THI### [at] hotmail com> wrote:
> There is an issue that's bugging me though. Can anyone tell me why I get
> a warning when I try to delete an array of sprite objects like so:
>
> delete [] tile;
Because it is the wrong thing to do?
> I initialized the array like this:
>
> hgeSprite *tile[8*8]
'tile' itself is not dynamically allocated. The elements contained in
'tile' however are pointers to dynamically allocated objects, and
therefore need to be deallocated individually.
--
FE
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Fredrik Eriksson wrote:
> On Fri, 12 Dec 2008 22:38:26 stbenge wrote:
>> I initialized the array like this:
>>
>> hgeSprite *tile[8*8]
>
> 'tile' itself is not dynamically allocated. The elements contained in
> 'tile' however are pointers to dynamically allocated objects, and
> therefore need to be deallocated individually.
I see. The array itself was not dynamic, so there was no reason to
delete it. Despite my limited knowledge of the subject, I managed to do
the right thing by deleting them individually... They were deleted in
the same manner as they were created. There's more to this issue I need
to explore, and I'm not ashamed to admit it ;)
Who's going to trust my programs, now that my ignorance is known? I
could always provide the source...
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge <THI### [at] hotmail com> wrote:
> hgeSprite *tile[8*8]
> and filled it like this:
> v=0;
> for(y=0;y<8;y++){
> for(x=0;x<8;x++){
> tile[int(v)]=new hgeSprite(tiles,x*132+1,y*132+1,128,128);
> v++;
> }
> }
Does the HGE library force you to write code like that? If so, then it
really sucks.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> stbenge <THI### [at] hotmail com> wrote:
>> hgeSprite *tile[8*8]
>
>> and filled it like this:
>
>> v=0;
>> for(y=0;y<8;y++){
>> for(x=0;x<8;x++){
>> tile[int(v)]=new hgeSprite(tiles,x*132+1,y*132+1,128,128);
>> v++;
>> }
>> }
>
> Does the HGE library force you to write code like that? If so, then it
> really sucks.
Don't use HGE to insult me... if that's what you are doing. I could have
written my loop a different way and used less code, but I feel
comfortable typing it out that way. I come from a
QBASIC/Euphoria/POV-Ray background, so I code the l_o_n_g way. Or do you
mean the way each sprite has to be made new? There might be better ways
to do it, for all I know.
I think a good programmer would be able make a large, clean program
using libraries that may have problems. I would like to be that
programmer, and I'm humble enough to admit that I'm not there. When it
comes down to it, all libraries and all languages are going to have
problems.
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge wrote:
> There is no goal to the game. You just fly around in a near-infinite
> landscape.
That's very Zen. With the fading trails, that's sort of like one of those
rice-paper pads you write on with water.
> If you are running Windows, have DirextX 8.0 or above, and have some
> free time, could you download it and tell me what you think? With the
> default settings it should run at 60 FPS regardless on your monitor's
> refresh rate.
I think mine is saying 75FPS or so. I'm on Vista x64, and it runs flawlessly.
> I would like some feedback on a few things. What is your operating
> system? Does it work on Vista? What FPS are you getting? Do the default
> settings of 1280x960 fullscreen mode give you ultra-smooth scrolling?
Yes.
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge <^@hotmail.com> wrote:
> Warp wrote:
> > stbenge <THI### [at] hotmail com> wrote:
> >> hgeSprite *tile[8*8]
> >
> >> and filled it like this:
> >
> >> v=0;
> >> for(y=0;y<8;y++){
> >> for(x=0;x<8;x++){
> >> tile[int(v)]=new hgeSprite(tiles,x*132+1,y*132+1,128,128);
> >> v++;
> >> }
> >> }
> >
> > Does the HGE library force you to write code like that? If so, then it
> > really sucks.
> Don't use HGE to insult me...
I was not insulting you. I was asking if that's the only way of using
HGE (because if it is, then HGE really sucks as a C++ library).
> I could have
> written my loop a different way and used less code, but I feel
> comfortable typing it out that way.
The problem is not the loop. The problem is the 'news' and 'deletes'.
While there are situations where their use is completely ok and in fact
the best thing to do, those situations are usually rare.
Usually the "correct" way of, for example, creating a certain amount
of objects like that is:
std::vector<hgeSprite> tile;
tile.reserve(8*8);
for(...)
for(...)
tile.push_back(hgeSprite(...));
(The word "correct" is deliberately in quotation marks because it's,
quite naturally, not *always* the best way to do it. However, in most
cases this - or something similar - is.)
However, depending on how hgeSprite has been designed, that might not
be possible. For example, if it doesn't have a properly working copy
constructor (or it has been disabled), then that method cannot be used.
If that's the case, then you indeed have to either allocate things with
'new', or use other dirty tricks to get it working. And it would also be
a sign of sloppy design in the HGE library.
The problem with using 'new' and 'delete' like that is that it's very
error-prone. C++ has its quirks with memory allocation, and you just have
to learn to live with them if you want to use it. Encapsulation is the key.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> stbenge <^@hotmail.com> wrote:
>> Don't use HGE to insult me...
>
> I was not insulting you. I was asking if that's the only way of using
> HGE (because if it is, then HGE really sucks as a C++ library).
Then please accept my apology :)
>> I could have
>> written my loop a different way and used less code, but I feel
>> comfortable typing it out that way.
>
> The problem is not the loop. The problem is the 'news' and 'deletes'.
> While there are situations where their use is completely ok and in fact
> the best thing to do, those situations are usually rare.
In the HGE tutorials, sprites are created in this manner, so I adapted
the method to work with arrays.
> Usually the "correct" way of, for example, creating a certain amount
> of objects like that is:
>
> std::vector<hgeSprite> tile;
>
> tile.reserve(8*8);
> for(...)
> for(...)
> tile.push_back(hgeSprite(...));
>
> (The word "correct" is deliberately in quotation marks because it's,
> quite naturally, not *always* the best way to do it. However, in most
> cases this - or something similar - is.)
>
> However, depending on how hgeSprite has been designed, that might not
> be possible. For example, if it doesn't have a properly working copy
> constructor (or it has been disabled), then that method cannot be used.
> If that's the case, then you indeed have to either allocate things with
> 'new', or use other dirty tricks to get it working. And it would also be
> a sign of sloppy design in the HGE library.
Thank you very much for this tip. I seem to remember that hgeSprite does
have a functioning copy constructor, so I should give the vector method
a go. Before I get there, I have to gain a better understanding of OOP
programming so I don't find myself hacking and experimenting with the
std::vector functions. Too much of my time is spent doing this, I'm
ashamed to admit :(
> The problem with using 'new' and 'delete' like that is that it's very
> error-prone. C++ has its quirks with memory allocation, and you just have
> to learn to live with them if you want to use it. Encapsulation is the key.
Another reason to get myself familiarized with all that C++ has to
offer. Thank you for your feedback!
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New wrote:
> stbenge wrote:
>> There is no goal to the game. You just fly around in a near-infinite
>> landscape.
>
> That's very Zen. With the fading trails, that's sort of like one of
> those rice-paper pads you write on with water.
I have no idea what you're talking about :)
>> If you are running Windows, have DirextX 8.0 or above, and have some
>> free time, could you download it and tell me what you think? With the
>> default settings it should run at 60 FPS regardless on your monitor's
>> refresh rate.
>
> I think mine is saying 75FPS or so. I'm on Vista x64, and it runs
> flawlessly.
>
>> I would like some feedback on a few things. What is your operating
>> system? Does it work on Vista? What FPS are you getting? Do the
>> default settings of 1280x960 fullscreen mode give you ultra-smooth
>> scrolling?
>
> Yes.
Thank you for trying it! You're input is valuable, considering my
girlfriend could not get it working on her Vista x64 laptop. It may be a
screen/video driver problem though. One of these days I'll have to make
a new MetroidVania :) (and finish it)
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge wrote:
>> That's very Zen. With the fading trails, that's sort of like one of
>> those rice-paper pads you write on with water.
>
> I have no idea what you're talking about :)
http://www.amazon.com/Buddha-Board/dp/B0010TEFFQ/ref=pd_bbs_sr_1
It's a dark board with rice paper over it. When you paint with water, it
leaves marks, but only till the water dries.
One I did as a java VADz a long time ago:
http://home.san.rr.com/dnew/ZenMaster.html
> Thank you for trying it! You're input is valuable, considering my
> girlfriend could not get it working on her Vista x64 laptop. It may be a
> screen/video driver problem though.
Possibly. This has one of those high-end 5.0 Vista cards, a Radeon HD 4850.
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge <THI### [at] hotmail com> wrote:
> Hi everyone,
>
> Months ago I updated my NearInfinity demo, but never got around to
> posting it. It can be found here:
> http://www.caltel.com/~abenge/NearInfinity.zip
>
> It should look like this:
> http://www.caltel.com/~abenge/NIscreen.jpg
>
> There is no goal to the game. You just fly around in a near-infinite
> landscape. I would call it infinite, but even the best random number
> generators don't go on forever. The landscape is not different every
> time you start the program up. You can change the landscape's seed
> within an .ini file. To make it a little more interesting, there are six
> Easter eggs to find. They occur very rarely, and you get no reward for
> finding them; they are simply there. Look at ee.png for spoilers :) Keys
> are left, right, up and/or z. It has bouncy and smooth, but solid
> collision-detection.
>
> If you are running Windows, have DirextX 8.0 or above, and have some
> free time, could you download it and tell me what you think? With the
> default settings it should run at 60 FPS regardless on your monitor's
> refresh rate. I kept the logic separate from the frame rate, so it
> should work on your computer exactly as it does mine, as long as your
> computer isn't really old. It will skip frames to compensate if your
> computer is slow.
>
> I would like some feedback on a few things. What is your operating
> system? Does it work on Vista? What FPS are you getting? Do the default
> settings of 1280x960 fullscreen mode give you ultra-smooth scrolling?
> What about lower resolutions? You can change these and other settings
> within the NI.ini text file. If you decide to run in windowed mode,
> change dynamicFrameRate to method 2. You won't get the benefits of
> VSYNC, and it really shows.
>
> If for some reason I didn't upload the program with the default 1280x960
> resolution and VSYNC settings, change the values found in NI.ini to these:
> windowed=0
> screenMode=2
> dynamicFrameRate=3
>
>
> Thanks in advance!
>
> Sam
It works on Vista x64 on quad core, very smooth scrolling.
It fails on XP home x32 dual core amd. Here is th elog file:
HGE Started..
HGE version: 1.70
Date: 14.12.2008, 16:32:00
Application: NearInfinity 2008 Sam Benge
OS: Windows 5.1.2600
Memory: 981480K total, 214936K free
D3D Driver: ati2dvag.dll
Description: ATI RADEON XPRESS 200 Series
Version: 6.14.10.6571
Can't find appropriate full screen video mode
Finishing..
The End.
Finishing..
The End.
It will work in 640x480 mode.
Interesting game, very good.
Isaac.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New wrote:
> stbenge wrote:
>>> That's very Zen. With the fading trails, that's sort of like one of
>>> those rice-paper pads you write on with water.
>>
>> I have no idea what you're talking about :)
>
> http://www.amazon.com/Buddha-Board/dp/B0010TEFFQ/ref=pd_bbs_sr_1
>
> It's a dark board with rice paper over it. When you paint with water, it
> leaves marks, but only till the water dries.
Sounds like a neat idea. I'm guessing this works because water tension
causes the rice paper to stick to a black backing? Simple, effective,
but possibly fragile for young ones :(
> One I did as a java VADz a long time ago:
> http://home.san.rr.com/dnew/ZenMaster.html
Pretty cool. There's a gap at the end of a drawing run if the mouse is
moving too fast. I assume you used an array to hold the positions/states
of each circle? It must be a big array because I never found the end.
>> Thank you for trying it! You're input is valuable, considering my
>> girlfriend could not get it working on her Vista x64 laptop. It may be
>> a screen/video driver problem though.
>
> Possibly. This has one of those high-end 5.0 Vista cards, a Radeon HD 4850.
Ack, HGE's hit-and-miss functionality is somewhat discouraging :(
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Clarence1898 wrote:
> stbenge <THI### [at] hotmail com> wrote:
>> I would like some feedback on a few things.
>
> It works on Vista x64 on quad core, very smooth scrolling.
> It fails on XP home x32 dual core amd. Here is th elog file:
> ...
> Can't find appropriate full screen video mode
>
> It will work in 640x480 mode.
Sounds like a simple video card shortcoming to me. I'll keep this in
mind if I make another game. Low-resolution video modes should always
work, so I'll have to keep new games at 640x480 or 320x240. A lot of
people hate these lower resolutions, but for 2D games they seem to work
well. Cave story, for instance, has a large fan base. It used a low
resolution and the scrolling wasn't smooth at all. People still loved it
because the game play more than made up for the lack of graphics quality.
> Interesting game, very good.
Thank you, Isaac. I would like to make enemies that you could kill, but
using this method to do it is looking impossible. The stars and motes
will always be the same if you go back to them, just like the landscape.
It would be hard to do this with enemies, because the method I used
doesn't allow me to change elements. They are all always static, though
in the case of the motes it doesn't seem that way.
Sam
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge wrote:
> Sounds like a neat idea. I'm guessing this works because water tension
> causes the rice paper to stick to a black backing?
Plus, the paper is thin enough that wetting it makes it more transparent.
> Simple, effective, but possibly fragile for young ones :(
Depends how young, I guess.
> Pretty cool. There's a gap at the end of a drawing run if the mouse is
> moving too fast. I assume you used an array to hold the positions/states
> of each circle? It must be a big array because I never found the end.
It is, of course, dynamically allocated. The fun thing is to compare the
size of the .class file with what you could build by hand. VADz is an old
technology to compress all kinds of interactive graphics down to a
relatively tiny size - we could put online order forms in a banner ad
smaller than most people could fit an advertisement in an animated GIF.
> Ack, HGE's hit-and-miss functionality is somewhat discouraging :(
It is strange. You'd think it would work or not. Maybe you're using some
feature which has to be supported by the card to work?
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> std::vector<hgeSprite> tile;
>
> tile.reserve(8*8);
> for(...)
> for(...)
> tile.push_back(hgeSprite(...));
Not that I'd recommend micro-optimization over readability, but isn't this
going to be more inefficient than the other way? Doesn't this allocate a
sprite on the stack, pass it to tile.push_back(), which then has to copy it
to dynamically allocated memory anyway?
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
stbenge <THI### [at] hotmail com> wrote:
> Who's going to trust my programs, now that my ignorance is known? I
> could always provide the source...
It's good to know you suck at least on something, Master. Makes you... more
human. ;)
OTOH, it's a pretty good-looking "game" for someone still learning its C++
way...
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> Warp wrote:
> > std::vector<hgeSprite> tile;
> >
> > tile.reserve(8*8);
> > for(...)
> > for(...)
> > tile.push_back(hgeSprite(...));
> Not that I'd recommend micro-optimization over readability, but isn't this
> going to be more inefficient than the other way? Doesn't this allocate a
> sprite on the stack, pass it to tile.push_back(), which then has to copy it
> to dynamically allocated memory anyway?
The std::vector reserves an entire array of objects, rather than
reserving them one-by-one. (What the reserve() function does is that it
allocates space for that many objects, without actually instantiating them.)
So rather than having 64 allocations with 'new', there's only one.
The temporary given as parameter is created only once, not 64 times.
What is done 64 times is calling the copy constructor of hgeSprite
(for each element added to the vector). It of course depends on how
the copy constructor in question is implemented, but usually calling
a copy constructor should not be significantly slower than calling the
regular constructor.
If we compare the amount of operations in both situations, they would be:
- 1 'new' call, vs. 64 'new' calls.
- 1 temporary instantiated, vs. none.
- 64 copy constructor calls, vs. 64 regular constructor calls.
- 1 temporary destruction, vs. none.
Doing only 1 'new' rather than 64 might in itself make the whole thing
faster, even though there's an additional temporary creation/destruction.
Anyways, even if there is some speed penalty, it's probably negligible.
As a reward your objects will be managed by std::vector, rather than you
having to manage them manually.
(As an additional reward, the std::vector will require less memory than
the 64 individually allocated objects. Of course with 64 objects this is
nothing, but it might become significant with a larger number.)
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> The std::vector reserves an entire array of objects, rather than
> reserving them one-by-one.
Ok, cool. I would have guessed std::vector would allocate an array of pointers.
> The temporary given as parameter is created only once, not 64 times.
So the declaration is essentially hoisted from out of the loop? Makes sense.
> What is done 64 times is calling the copy constructor of hgeSprite
OK. I know the assignment operator, and the constructor, but I never heard
of a copy constructor. <google> Oh, I see. It's a constructor with a
particular type for the argument. OK.
> a copy constructor should not be significantly slower than calling the
> regular constructor.
It doesn't look like it, but doesn't it have to "construct" the temporary,
then invoke the copy constructor? It's not like the compiler can construct
directly into the array element that std::vector allocated, can it? And
wouldn't it have to call the destructor on the temporary 64 times after it
was copied (assuming it has a destructor)?
It looks like the code you wrote initializes a temporary, then passes it (by
address, presumedly) to std::vector.push_back(), which then has to copy it
into an internal array. Then the code reinitializes the same temporary for
the next iteration (assuming the code is even a little smartly compiled). If
the sprite has a destructor, doesn't it need to call that before freeing the
temporary? How can the compiler know the ... Oh, is std::vector inlined
enough that the compiler can see it's getting inserted directly into a
particular array element? If so, that would make sense, yes.
> If we compare the amount of operations in both situations, they would be:
>
> - 1 'new' call, vs. 64 'new' calls.
> - 1 temporary instantiated, vs. none.
> - 64 copy constructor calls, vs. 64 regular constructor calls.
> - 1 temporary destruction, vs. none.
Hmmm... How does the temporary get constructed? Wouldn't it be 64 calls to
the constructor (taking the various individual arguments for the sprite) and
then 64 calls to copy that data to the one that's in std::vector?
> Doing only 1 'new' rather than 64 might in itself make the whole thing
> faster, even though there's an additional temporary creation/destruction.
>
> Anyways, even if there is some speed penalty, it's probably negligible.
> As a reward your objects will be managed by std::vector, rather than you
> having to manage them manually.
As I said, I'm never (well, rarely) against clarity and idiom in favor of
micro-performance optimizations. Especially when you're talking about
initialization which you presumably do only once.
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > The temporary given as parameter is created only once, not 64 times.
> So the declaration is essentially hoisted from out of the loop? Makes sense.
Ok, it may be possible that it is created in each loop, depending on how
you are creating it, so I may have exaggerated that bit, and it may indeed
be created 64 times.
You could manually optimize it and create it only once outside the loops,
to make sure. (If, for example, each sprite is otherwise identical but with
different coordinates, you could simply change the coordinates and push it
into the vector.)
OTOH in this case it probably doesn't matter.
> > What is done 64 times is calling the copy constructor of hgeSprite
> OK. I know the assignment operator, and the constructor, but I never heard
> of a copy constructor. <google> Oh, I see. It's a constructor with a
> particular type for the argument. OK.
The difference between the copy constructor and an the assignment operator
is that in the latter case the object being assigned to has already been
constructed in the past, and now it's assigned a new value. With the copy
constructor the object in question is directly initialized with the given
object.
Copy constructors are called, for example, when you pass an object to
a function by value.
(In some situations the compiler may be able to optimize the copy
constructor call away.)
> > a copy constructor should not be significantly slower than calling the
> > regular constructor.
> It doesn't look like it, but doesn't it have to "construct" the temporary,
> then invoke the copy constructor? It's not like the compiler can construct
> directly into the array element that std::vector allocated, can it?
Actually it is perfectly possible to construct into an array element
directly. (A programmer can do that himself by using so-called placement
new.)
Whether or not the compiler is smart enough to optimize it like that
is another question.
> And
> wouldn't it have to call the destructor on the temporary 64 times after it
> was copied (assuming it has a destructor)?
If the temporary is created 64 times, then it will be destroyed 64 times
as well, of course.
> It looks like the code you wrote initializes a temporary, then passes it (by
> address, presumedly) to std::vector.push_back(), which then has to copy it
> into an internal array. Then the code reinitializes the same temporary for
> the next iteration (assuming the code is even a little smartly compiled). If
> the sprite has a destructor, doesn't it need to call that before freeing the
> temporary? How can the compiler know the ... Oh, is std::vector inlined
> enough that the compiler can see it's getting inserted directly into a
> particular array element? If so, that would make sense, yes.
Being a template class, all member functions of std::vector are inlined.
(This might change with export templates, but things like push_back() will
probably always be inline, for efficiency.)
As I said, you may be right in that the temporary is created 64 times,
though. You could hand-optimize it by creating it once outside the loops.
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Darren New <dne### [at] san rr com> wrote:
>>> The temporary given as parameter is created only once, not 64 times.
>
>> So the declaration is essentially hoisted from out of the loop? Makes sense.
>
> Ok, it may be possible that it is created in each loop, depending on how
> you are creating it, so I may have exaggerated that bit, and it may indeed
> be created 64 times.
I would think if the arguments are different each time, the constructor at
least would have to be called, even if it's overwriting the previous memory
locations. (I.e., if the compiler allocated the space once on the stack or
even in static memory.) And if the compiler can't tell the class's
destructor doesn't do something like calling 'destroy', it can't just
overwrite the constructed value without destructing it first?
> You could manually optimize it and create it only once outside the loops,
> to make sure. (If, for example, each sprite is otherwise identical but with
> different coordinates, you could simply change the coordinates and push it
> into the vector.)
Yep.
> OTOH in this case it probably doesn't matter.
Yep.
> Copy constructors are called, for example, when you pass an object to
> a function by value.
Good to know. Thanks! Still wading thru the C++ manuals and tutorials here.
> Actually it is perfectly possible to construct into an array element
> directly. (A programmer can do that himself by using so-called placement
> new.)
Sure. I was speaking of this particular bit of code, tho.
> Whether or not the compiler is smart enough to optimize it like that
> is another question.
OK.
>> And
>> wouldn't it have to call the destructor on the temporary 64 times after it
>> was copied (assuming it has a destructor)?
>
> If the temporary is created 64 times, then it will be destroyed 64 times
> as well, of course.
OK. So you were assuming a trivial destructor? I'm just trying to learn.
> As I said, you may be right in that the temporary is created 64 times,
> though. You could hand-optimize it by creating it once outside the loops.
I would bet that it's easier for the compiler to create the temporary only
once than it is for the compiler to know it can call the constructor on the
array elements in std::vector without any temporary needed at all. :-)
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Darren New <dne### [at] san rr com> wrote:
> > Copy constructors are called, for example, when you pass an object to
> > a function by value.
> Good to know. Thanks! Still wading thru the C++ manuals and tutorials here.
Copy constructors and assignment operators are important to know in C++
when designing classes.
Usually when your class only has basic types and managed classes as
members, you don't need to create a copy constructor nor assignment operator
for your class. The default ones generated by the compiler will usually
suffice. (The default ones simply copy/assign each member of the parameter
to each member of this.)
For example:
class MyClass
{
int x, y; // basic types
std::string name; // managed class
public:
// No need to write your own copy constructor / assignment operator
// here because the compiler-generated ones suffice.
...
};
(I think the term "managed" is not generally used in pure C++, but I'll
use that term because I can't think of any better one. Not to be confused
with managed classes in .NET; I'm not talking about those at all. With
"managed" I mean a class which may allocate memory inside itself, but is
well-behaved and can be safely copied and assigned around without worries.
std::string and the STL containers are examples.)
Problems happen when your class has, for example, raw pointers to memory
it has allocated, or other similar resources. Example:
class MyClass
{
int* array;
public:
MyClass(int size)
{
array = new int[size];
}
~MyClass() { delete[] array; }
// If now you don't implement a copy constructor / assignment operator
// you will have a problem!
};
How to solve the problem depends on how you want your class to behave.
By far the easiest (and most common) solution is to simply forbid copying
and assignment of your class. This happens by declaring the copy constructor
and assignment operator in the private part of the class and not implementing
them. Problem solved. Of course now you can't copy nor assign instances of
your class (nor can you use them in STL containers), but in many cases you
don't need to. This is very typical for classes which you instantiate only
once during the execution of the program.
What forbidding those functionalities does is prevent accidents, so it's
a good custom, even if you are not intending to copy/assign the class around.
Sometimes you do want to copy/assign instances of your class, though.
In that case you have to decide whether the copies should share the data
or deep-copy it. If deep-copying is enough, then by far the easiest solution
is replacing your array with a std::vector, and consequently you don't need
to write any copy constructor / assignment operator. If you want to share,
then you could use the smart pointers offered by the Boost library. (The
current C++ standard doesn't yet offer such smart pointers, except for
auto_ptr, which cannot be used for sharing data.)
Of course this is good only if your data is an array. If your dynamic data
has a more complex geometry, it becomes a bit more complicated.
Also the data might not be memory at all. For example this presents the
exact same problem:
class MyClass
{
std::FILE* inputFile;
public:
MyClass(): inputFile(0) {}
~MyClass()
{
if(inputFile) std::fclose(inputFile);
}
bool openFile(const char* filename)
{
if(inputFile) std::fclose(inputFile);
return (inputFile = std::fopen(filename, "rb"));
}
...
};
The data is not memory, but if you copy/assign instances of this class,
you'll get a problem (you may access or close a closed file).
A more subtle example:
class MyClass
{
std::list<Node> nodes;
std::list<Node>::iterator currentNode;
...
};
Assume 'currentNode' points to some node in the list, or to nodes.end().
If this class doesn't have a proper copy constructor / assignment operator
and you copy instances of it, you'll get problems.
I suppose the morale of this long text is: If you stick to basic types
and managed classes (as I call them) as far as possible, you'll avoid
problems. Pointers and iterators as members are often problematic.
(Note that sometimes you might want to forbid copying/assignment even
if the compiler-generated ones were safe. This may be the case eg. if
the copying would be a very heavy operation, such as if you have huge
vectors as member variables.)
--
- Warp
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> By far the easiest (and most common) solution is to simply forbid
> copying
> and assignment of your class. This happens by declaring the copy
> constructor and assignment operator in the private part of the class and
> not implementing them. Problem solved. Of course now you can't copy nor
> assign instances of your class (nor can you use them in STL containers),
> but in many cases you don't need to. This is very typical for classes
> which you instantiate only once during the execution of the program.
>
> What forbidding those functionalities does is prevent accidents, so it's
> a good custom, even if you are not intending to copy/assign the class
> around.
>
> Sometimes you do want to copy/assign instances of your class, though.
> In that case you have to decide whether the copies should share the data
> or deep-copy it. If deep-copying is enough, then by far the easiest
> solution is replacing your array with a std::vector, and consequently you
> don't need to write any copy constructor / assignment operator. If you
> want to share, then you could use the smart pointers offered by the Boost
> library. (The current C++ standard doesn't yet offer such smart pointers,
> except for auto_ptr, which cannot be used for sharing data.)
More info on this is available on Warp's website :)
http://warp.povusers.org/c++test/
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
Warp wrote:
> Copy constructors and assignment operators are important to know in C++
> when designing classes.
Yes. So far, that's really the only part that doesn't seem pretty obvious
just skimming thru tutorials. Hence the wading in seriously.
> (I think the term "managed" is not generally used in pure C++, but I'll
> use that term because I can't think of any better one.
Yeah. I wonder if MS actually defines the term "managed" clearly. :-) Other
than "that stuff .NET does for you."
Thanks. I'll come back and re-read this again when I get past the chapter
that talks about such stuff explicitly.
--
Darren New, San Diego CA, USA (PST)
The NFL should go international. I'd pay to
see the Detroit Lions vs the Roman Catholics.
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|  |
|
 |
> I see. The array itself was not dynamic, so there was no reason to delete
> it. Despite my limited knowledge of the subject, I managed to do the right
> thing by deleting them individually... They were deleted in the same
> manner as they were created. There's more to this issue I need to explore,
> and I'm not ashamed to admit it ;)
>
> Who's going to trust my programs, now that my ignorance is known? I could
> always provide the source...
Hehe I had something similar where I had forgotten to type the "()" after a
release call on a texture object (this is DirectX). For some reason the
compiler though that:
texture1->Release;
was a perfectly fine statement, but of course it wasn't calling the release
function to free up the memory. It took me ages to track this one down,
because quickly reading that line you don't notice the () missing, and it
just caused random crashes occasionally on exit.
Took me a while to figure out the whole delete [] palaver too.
Oh well, you live and learn :-)
Post a reply to this message
|
 |
|  |
|  |
|
 |
|
 |
|  |
|
 |