The problem
I have this structure that I want to create a "constructor" for it.
struct example {
int x, y, z; /* various members */
struct another *another; /* pointer to another structure */
}
The two different ways I know of
- Using functions that create and delete structures on heap
I can create structures by making functions that allocates them on the heap like this:
/* create example */
struct example *example_new(int x, int y, int z) {
struct example *p = malloc(sizeof *p);
p->x = x;
p->y = y;
p->z = z;
p->another = another_new();
}
/* delete example */
void example_new(struct example *p) {
another_del(p->another);
free(p);
}
- Using functions that initializes and frees memory for structure through pointer
Or I could initalize the structure by passing pointer to the function and have the user responsible for allocating and deallocating memory for it.
/* initalize example */
void example_init(struct example *p, int x, int y, int z) {
assert(p);
p->x = x;
p->y = y;
p->z = z;
p->another = another_new();
}
/* free memory allocated for example */
void example_free(struct example *p) {
another_del(p->another);
}
My thoughts
I usually prefer first approach when creating recursive structures (like Trees and Linked Lists) but use the second approach in all other cases.
I tried to use the second approach for a recursive structure and it turned out to be quite messy.
The question
How do you choose between these two ways? Is there a third way you would use to solve this problem?
Best Answer
There is no answer that's always going to be the right one, because different programs will have different needs. If you have enough information to make an informed call one way or the other, that's the way you should go. (For example, if your program is required to have the highest throughput possible, forcing a
malloc()
/free()
pair each time some function needs to use your structure probably isn't going to fly.)If you're going to build a general-purpose library, it isn't much additional effort to build it in a way that supports both:
void example_init(struct example *p, int x, int y, int z)
- Initializes the structure pointed at byp
. This can be called by anyone who has astruct example
whether it's an automatic, allocated on the heap or pulled out of a pool.void example_destroy(struct example *p)
- Does whatever is necessary to de-initialize the structure pointed at byp
. (In your case, this would be freeing the memory allocated for theanother
member.) Note that it does not freep
, because it has no knowledge of whether or not the structure is on the heap or if the caller isn't going to re-use it by callingexample_init()
again.struct example * example_new(int x, int y, int z)
- Allocates space for the structure on the heap, callsexample_init()
against it and returns the pointer.void example_del(struct example * p)
- Callsexample_destroy(p)
and then freesp
.There is a hazard to this approach, which is that someone could try to do pairs of
example_init()
/example_del()
orexample_new()
/example_destroy()
calls. Tools likevalgrind
and some implementations offree()
can weed those problems out automagically. Another approach would be to set aside a bit somewhere in the structure that can be set when the structure was on the heap and use assertions to complain if someone goes about it the wrong way. (example_del()
would need to clear the bit before callingexample_destroy()
.)