- copy-by-value shouldn't be used on them (lest we want "cojoined twins" copies), having them as pointers ensures that I am willingly doing a copy-by-reference or that, if I want a deep copy, I have implemented a function that copies members recursively.
- If I ever put values of these structs in an array, I will probably want to make it an array of pointers to these values instead of storing them directly. Two reasons for this:
- The cost of moving a pointer is less than the cost of moving a struct that also contains pointers.
- If it is a dynamic array, I will have to move them around, breaking external references to these elements if I don't have this extra level of indirection.
- Similar to the last one, working with pointers to these values and always allocating these in heap ensures that they will preserve the same position in memory during their whole lifetime, ensuring that external references to it remain valid even if they are passed around.
I'll admit most of these points seem premature, especially for this structure.
It feels nice to have a "free" or "cleanup" function that also frees the whole object instead of leaving it with dangling pointers, in case we neglect setting them to NULL.
If makes clear that the owner of the members (and who is responsible their memory) is the object itself and not the object that contains it:
Given
struct bar { struct apple *a; struct banana *b; };
consider
struct foo { struct bar smol; // Not clear whether "smol" or "big" is the owner of "a" and "b". } big;
versus
struct foo { // it is clear that only "smol" is the owner of "a" and "b" and it should free their memory. struct bar *smol; } big;
- Which I think also discourages a little the practice of implicitly transferring ownership.
I don't quite understand this. Idiomatically, in the embedded struct case, big_cleanup() / big_destroy() would call small_cleanup(&big->small) directly, which would itself call apple_destroy(), banana_destroy().
Even if there are cases where applying this heuristic is not justified, I feel it makes the code more robust for changes that may appear in the future.
Of course, I am willing to revert it when the reviewers don't like it! :)
I don't hate it, it just didn't quite seem like the most obvious thing. If you feel strongly about it I don't mind keeping it a pointer.