Managing memory in a compound template
I have run into the same problem over and over in recent weeks. Rolling back to my core, I build a (directed acyclic) hierarchy of objects like:
-
a -> c
-
b -> c
-
b -> d
An instance can have more than one parent and more than one child. c
can be of value to readers and writers. b
can be a compound value. The hierarchy is easily created with a factory - eg. a.setChild(new C())
... Later, the client only cares about the root on which she calls getValue()
or setValue()
.
My question is, who cleans up the hierarchy? Who is in charge of calling delete
on b
or c
?
-
Option. factory created nodes, factory has to remove nodes: I don't like this setting because I understand factory as a replacement for
new
. It's weird to keep the factory going until the instances it creates are destroyed. -
Option - Smart Pointers: Much less good because it pollutes the interface and introduces more complexity for simple things like pointers.
-
Option - grafopodobny class that performs memory management: The class gathers all the nodes
a
,b
,c
,d
, ... and provides access to the root. The nodes themselves refer to each other, but do not remove children or parents. If the "graph" or compound manager is destroyed, it destroys all nodes.
I prefer the latter option. This, however, complicates the building of the hierarchy. Either you create a hierarchy outside and tell the graph about all nodes, or you build a hierarchy inside the graph that smells like option 1. The graph can only delete what it knows. Therefore memory leaks are somehow in design if you need to pass nodes to the graph.
Is there a pattern for this problem? Which strategy do you prefer?
Edit 1 - September 1, 2014: Sorry for not being smart pointers specific. I have tried to avoid another "when to use smart pointer question" and focus on alternative solutions instead. However, I am willing to use smart pointers if they really are the best option (or if necessary).
In my opinion, signature setChild(C* child)
should be preferred setChild(std::shared_ptr<C> child)
for the same reason as for each loop, which should be preferred over iterators. However, I have to admit that, like std:string
, the generic pointer is more definite regarding its semantics.
In terms of complexity, every operation within a node must now deal with shared pointers. std::vector<C*>
becomes std::vector< std::shared_ptr<C> >
, ... Also, each pointer contains a reference count around, which could have been avoided if there were other parameters.
I must add that I am developing the low-level part of the real-time system. This is not a firmware, but a closure.
Edit 2 - Sep 1, 2014: Thanks for all the input. My particular problem is this: I am getting a byte array giving the sensor data. At some point, I am told what value is being written in this array. On the one hand, I want to have a mapping from a position in an array with a primitive value (int32, double, ...). On the other hand, I want to combine primitive values into complex types (structures, vectors, ...). Unfortunately, not all views are bidirectional. For example. I can read the comparison of values, but I cannot write the values according to the result of the comparison. So I separated readers and writers and allowed them to get the same meaning, if necessary.
source to share
Option - "Smart" pointers: much less, because it pollutes the interface, and introduces more complexity for simple things like pointers.
Smart pointers are memory-managed pointers, which is exactly what you need.
You are not obligated to expose smart pointers in your interface, you can get a raw pointer from smart as long as the object is still owned by the smart pointer somewhere (note that this is not necessarily the best idea though, smart pointers in the interface far from ugly).
By actually exposing the original pointers in your interface, you introduce a lot more pollution and complexity indirectly because you need to document the ownership and life rules that are explicit when using smart pointers, making them even more convenient than "plain pointers".
And this is most likely a "futuristic" way of doing things: since C ++ 11/14 smart pointers are part of the standard, std::string
would you say that it std::string
pollutes the interface and introduces more complexity compared to const char*
?
If you have performance issues, the question is, will your handcrafted solution, an alternative solution, be more performant (this needs to be measured), and is it worth the development time required to develop the feature and maintain the code
source to share