• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
StoneMask

Is it acceptable to call a destructor?

40 posts in this topic

I read into it a bit, and the little information I got was from [url="http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr380.htm"]this source[/url]:

[quote]You can use a destructor explicitly to destroy objects, although this practice is not recommended.[/quote]

I have an [b]enemy [/b]object that is destroyed upon being intersected with a [b]projectile [/b]object. Instead of making a bunch of if statements in a for loop outside the object, wouldn't it be easier and cleaner to make the enemy object just destroy itself if it detects colliding with a projectile? Would it be okay, according to conventions, to call ~enemy INSIDE the enemy class, rather than having to code outside the enemy class? Edited by StoneMask
0

Share this post


Link to post
Share on other sites
In general, it's poor form to directly call a destructor (save for the case where you're calling a parent class destructor from a child class desctructor).

Honestly I've never heard of anyone doing this and I can think of a lot of things that could go wrong. For instance, if after your ~Enemy() call, you attempt to manipulate the object (either outside the class or inside the class itself), you'll have an invalid pointer.

For instance:

[source lang="cpp"]...
// NOTE: Assumes you have a vector/array of enemies and a pointer to the projectile.

for( int i = 0; i < numEnemies; ++i )
{
if( checkCollision( enemies[i], pProjectile ) )
{
// Destructor will be called right here in onCollision().
enemies[i].onCollision(pProjectile);
}

// Now you have an invalid reference to your object and this call will produce only sadness (crash or undefined behavior).
enemies[i].someOtherFunction();
}

...[/source]
0

Share this post


Link to post
Share on other sites
Also, keep in mind the difference between allocation/deallocation and construction/destruction. If the enemy holds projectile objects (e.g. an std::vector<projectile>) you don't need to do anything extra, all destructors are called automatically. But if it holds object *pointers* (e.g. std::vector<projectile*>) you need to decide on ownership. It's usually a good idea keep allocation and deallocation responsibility together. If it's the enemy object that allocates and stores projectiles, it should likely deallocate them too in its destructor.

[edit]

Ah, reading the OP I see the enemy doesn't hold projectiles. However, the principle that keeping allocation and deallocation together is still good advice here. And in any case, neither the birth and death of an object instance should be controlled by that instance itself. The former is just counter-intuitive and the latter too controversial. Edited by Felix Ungman
1

Share this post


Link to post
Share on other sites
[quote name='alvaro' timestamp='1350588033' post='4991525']
[Waits for someone to post some std::remove_if invocation in response to BeerNutts's code...]
[/quote]
That was my first reaction as well:
[source]
EnemyList.erase(
std::remove_if(
std::begin(EnemyList),
std::end(EnemyList),
[](Enemy &e) {return e.Update() == ENEMY_DEAD;}),
std::end(EnemyList));
[/source]
2

Share this post


Link to post
Share on other sites
Ofc you can use it that way. It will looks smth like this:
We run loop on an array of some objects to check some abstract requirement.
[source]#include <stdio.h>
#include <list>
class A {
public:
A() {
printf("A:constructor.\n");
}
~A() {
printf("A:destructor.\n");
}
};
int main() {
std::list<A*> objects;
printf("Allocate 10 objects of A\n");
for (int i = 0; i < 10; i++)
objects.push_back(new A());

printf("Time to die\n");
int list_size = objects.size();
for (int i = 0; i < list_size; i++) {
A* deadbeef = *(objects.begin());
objects.erase(objects.begin());
delete deadbeef;
}
printf("Elements in list: %d\n", objects.size());
}
[/source]
Output:
[CODE]
Allocate 10 objects of A
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
Time to die
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
Elements in list: 0
[/CODE]

But this is now clever 'cause memory allocation is kinda slow operation. Much better if we will save memory for such type of object and use it letter.

[source]
#include <stdio.h>
#include <list>
class A {
public:
A() {
printf("A:constructor.\n");
}
~A() {
printf("A:destructor.\n");
}
void Clear() {
}
};
class AAlloc {
std::list<A*> _cache;
public:
A* Alloc() {
if (!_cache.empty()) {
A* deadbeef = _cache.front();
_cache.erase(_cache.begin());
deadbeef->Clear();
return deadbeef;
}
return new A();
}
void Dealloc(A* obj) {
_cache.push_back(obj);
}
~AAlloc() {
std::list<A*>::iterator itr = _cache.begin();
for ( ; itr != _cache.end(); ++itr) {
delete *itr;
}
}
};
int main() {
std::list<A*> objects;
AAlloc allocator;
printf("Allocate 10 objects of A\n");
for (int i = 0; i < 10; i++)
objects.push_back(allocator.Alloc());

printf("Time to die\n");
{
std::list<A*>::iterator itr = objects.begin();
for ( ; itr != objects.end(); ++itr) {
allocator.Dealloc(*itr);
}
objects.clear();
}
printf("One more allocation of 10 objects\n");
for (int i = 0; i < 10; i++)
objects.push_back(allocator.Alloc());
printf("IT\'S A GOOD DAY TO DIE!\n");
{
std::list<A*>::iterator itr = objects.begin();
for ( ; itr != objects.end(); ++itr) {
allocator.Dealloc(*itr);
}
objects.clear();
}
printf("Elements in list: %d\n", objects.size());
}
[/source]

New output:
[CODE]
Allocate 10 objects of A
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
A:constructor.
Time to die
One more allocation of 10 objects
IT IS A GOOD DAY TO DIE!
Elements in list: 0
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
A:destructor.
[/CODE]

It's maybe looks pretty tricky but the main reason is speed up application. Edited by AlexB.hpp
-3

Share this post


Link to post
Share on other sites
It's probably preferable in this case to do some kind of mark-and-sweep pattern (As Brother Bob showed). Its possible to call a destructor safely, but only in very specific circumstances.
1

Share this post


Link to post
Share on other sites
Not trying to hijack the thread here but placement new isn't useless or "almost never used". Lots of large games need their own memory manager with a smart point/reference system and want their objects to be allocated in their memory management system's contiguous, managed heap. Thus placement new operator is used to put them there rather than in statically allocated bits of memory. I've done this before myself.

But I agree that use of placement new is not common; it's simply not necessary most of the time. But in large, AAA games that can be dealing with several GB of memory it's often a good idea to have a memory manager and use placement new.
0

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1350599800' post='4991582']
[source]
auto pred = [](Enemy &e) {return e.Update() == ENEMY_DEAD;};
auto last = remove_if(begin(EnemyList), end(EnemyList), pred);

EnemyList.erase(last, end(EnemyList));
[/source]
[/quote]
Too many C++0x. I guess it's difficult for novices. Don't you think so?

But however your code show me some interesting points. Thanks
0

Share this post


Link to post
Share on other sites
[quote name='AlexB.hpp' timestamp='1350600422' post='4991585']
[quote name='Brother Bob' timestamp='1350599800' post='4991582']
[source]
auto pred = [](Enemy &e) {return e.Update() == ENEMY_DEAD;};
auto last = remove_if(begin(EnemyList), end(EnemyList), pred);

EnemyList.erase(last, end(EnemyList));
[/source]
[/quote]
Too many C++0x. I guess it's difficult for novices. Don't you think so?

But however your code show me some interesting points. Thanks
[/quote]
Both yes and no.

Yes, the whole solution is too much to digest for a beginner. For a beginner of programming that is, but not necessary for a beginner of C++ that has some basic experience with other languages.

No, I don't think the C++11 parts of my solution makes it difficult. Very much the opposite in fact. Consider the variable declarations that would be necessary for [i]pred[/i] and [i]last[/i] if it wasn't for the [i]auto [/i]keyword. Consider the beauty of specifying code directly where it is used instead of somewhere else.

With reservation for some syntax errors of course, even VS2010 with it's very limited C++11 support can compile it. There's really nothing exceptional and brand new about it.
2

Share this post


Link to post
Share on other sites
Yeah, I have used a similar procedure for collisions and deallocation before. Something like the following:

[CODE]
// at some point in code
projectile missile;
new enemy[num];

// check for enemy collisions
for (short i = 0; i < num; i++)
{
enemy[i].move();

if (enemy[i].collide(missile) == true)
delete [i] enemy;
}

[/CODE]

Please note that this is highly abridged. My game's world is actually based around a character array, so I don't even have to pass the object, just a character value.

Thanks for all your help though; I understood when BeerNutts mentioned the increment step being messed up because of calling the destructor. Edited by StoneMask
0

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1350578992' post='4991477']
How do you ensure that the destructor isn't called again when the object is actually about to be destroyed (for example goes out of scope, you release the memory by calling delete, or removing the object from an std::vector)?

What's wrong with having the outside logic taking care of that? If the enemy destroys itself, then that kind of implies that the enemy itself is responsible to handling the collision with projectiles. Isn't that a job for some outside component to handle collisions, like a physics component or something?
[/quote]
Exactly what Brother Bob says; why would you allow an object to self destruct? If the enemy class had a bool collision(vec3 from_pt, vec3 to_pt) and returned true
for collision, you could handle destruction just after handling the bullet physics.
To separate your subsystems collision, game logic, models etc, you'd probably want to go with those bloody iterations, flags and internal messaging systems anyways,
but it's "nicer" to delete or std::pop() / remove than it is to selfdestruct. IMO.

EDIT: Oh, I just noticed that you've taken it a few steps further. I will leave you two to it. :) Edited by SuperVGA
0

Share this post


Link to post
Share on other sites
Are you saying I put it a step further, lol?

I'm unfamiliar with the syntaxes some of you are giving me, or I don't really find them very clean/readable (no offense). So to me it looks like I'm doing [b]less [/b]work or I'm lacking in some area of efficiency or functionality. I kind of think that way whenever I run into an unfamiliar syntax or code that looks intimidating to read through.

Compared to the methods that have been discussed in the thread compared to my posted way of doing it, which is the best in terms of efficiency and cleanliness? In C++ or something similar, please. Or even pseudocode! Edited by StoneMask
0

Share this post


Link to post
Share on other sites
[quote name='Bregma' timestamp='1350671577' post='4991860']
I would argue Brother Bob's way is most efficient and clean, and has the most elegant and clean syntax. Then again, I'm used to reading C++.

Laerning how to use the standard library (std::remove_if(), std::vertex()) and the language features (lambdas, auto type deduction) is the boss fight that will unlock the next level.
[/quote]

The problem is this is a beginner's forum, and when you start putting lambda functions, using std::bind, functors, or any other somewhat advanced techniques, it's easy to get discouraged. As you said, you're used to looking at C++ (and, probably used to looking at C++11).

Do you really expect a beginner, who is just learning what a for loop does, to understand a lambda function?

So, IMO, this forum should stick to fundamentals and being obvious about how to conquer problems, and this would exclude any C++11.

The boss fight is for the experienced players, not someone who just picked up the joystick for the first time.
2

Share this post


Link to post
Share on other sites
At the level you're working at, you do not call destructors.

Normally people do not call the destructors. Since C++ is clearly pretty new to you, take the advice and do what everyone else does and don't call the destructors directly. Because if you do, you'll be back here in a week asking people to debug the weird crashes in your program and that road is going to be really hard work because suddenly you're in a place where very few people will be able to help you because everyone else doesn't call the destructors because they don't want to be in that place.

There is a reason people learning to juggle start with bean bags and not running chainsaws. It's less exciting, that's for sure. But it's also less [i][b]exciting.[/b][/i]
2

Share this post


Link to post
Share on other sites
[quote name='BeerNutts' timestamp='1350673209' post='4991865']
[quote name='Bregma' timestamp='1350671577' post='4991860']
I would argue Brother Bob's way is most efficient and clean, and has the most elegant and clean syntax. Then again, I'm used to reading C++.

Laerning how to use the standard library (std::remove_if(), std::vertex()) and the language features (lambdas, auto type deduction) is the boss fight that will unlock the next level.
[/quote]

The problem is this is a beginner's forum, and when you start putting lambda functions, using std::bind, functors, or any other somewhat advanced techniques, it's easy to get discouraged. As you said, you're used to looking at C++ (and, probably used to looking at C++11).

Do you really expect a beginner, who is just learning what a for loop does, to understand a lambda function?

So, IMO, this forum should stick to fundamentals and being obvious about how to conquer problems, and this would exclude any C++11.

The boss fight is for the experienced players, not someone who just picked up the joystick for the first time.
[/quote]
Fundamental does not equate simple. Everything in every language, literally, is just syntactic sugar on top of machine code that adds a levels of abstraction. The more of this sugar you exclude, the more of other things you have to learn instead. I don't think manually erasing elements is any more obvious than using functions that the standard library already provides.

I have already responded to an earlier poster that I agree that my solution [i]as a whole[/i] may be too much for a beginner (and frankly, I only posted it because alvaro asked for it). But I also argued that, once you have accepted the solution in general, the C++11 features I included actually [i]improves[/i] the simplicity of it.

Ignoring the C++11 features because they add complexity is wrong in my opinion. You can ignore my solution as a whole, but it would be outright stupid to remove the C++11 from it because that's going to turn it into a mess (or an even greater mess, if you like).
0

Share this post


Link to post
Share on other sites
[quote name='BeerNutts' timestamp='1350673209' post='4991865']
So, IMO, this forum should stick to fundamentals and being obvious about how to conquer problems, and this would exclude any C++11.
[/quote]

I love the irony.

This sort of thing [i]isn't even a problem[/i] in the languages we regularly recommend to beginners. Maybe we should focus on excluding C++ altogether?

If we're going to include C++, then include it properly. Edited by Telastyn
1

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  
Followers 0