Although nodes are created in the usual C++ fashion, the procedure for deleting nodes differs from the C++ style. The following discussion explains how a node counts references to itself and when these references are incremented and decremented. It outlines the proper procedure for unreferencing a node, which results in the node's deletion.
Engines also store a reference count (see Chapter 15, Engines). This count is incremented when the output of an engine is connected to a field. You can also increment or decrement the reference count manually, by calling ref() or unref().
Figure 3.13, “ Reference Counts ” shows the reference counts for nodes in a small subgraph. Whenever you create a reference to a node, you increment its count. The action
A->addChild(B)
adds node B to node A and also increments the reference count for node B by 1. In Figure 3.13, “ Reference Counts ” node C has a reference count of 2 because it has been added to two different parent groups. At this point, nodes A and D contain 0 references.
Referencing a node in a path also increments the node's reference count, as shown in Figure 3.14, “ Incrementing the Reference Count ”. The reference count for node A now becomes 1, and the reference count for node B becomes 2.
When you remove a reference to a node, its reference count is decremented. Removing a child decrements the reference count. When a node's count returns to 0, it is deleted from the database. Consider the following cases, however, where deleting a node causes problems (refer to Figure 3.13, “ Reference Counts ” for this discussion):
Problem 1: | If you remove node B from node A, the reference count for node B goes to 0 and the node is deleted. But what if you still want to use node B? |
Problem 2: | How do you delete node A? Its reference count has always been 0. |
Problem 3: | What if someone applies an action to a node that has a reference count of 0? The action creates a path, which references the node. When the action finishes, the path is removed, and the node is deleted. |
B->ref();
Referencing a node increments its count by 1 and ensures that the node is not accidentally deleted. After you have explicitly referenced node B, you can safely remove it as a child of A without fear of deleting node B (Problem 1).
Similarly, to prevent node A from being deleted (Problem 3), you reference it:
A->ref();
If you want to delete A (Problem 2), you can unreference it, which decrements the reference count. Node A is now deleted, since you were the only one with a reference to it:
A->unref();
When a group is deleted, all of its children are removed and their reference counts are decremented by 1. In Figure 3.15, “ Decrementing the Reference Count ”, for example, if you specify
P->unref(); // reference count for P goes to 0
the reference counts for the child nodes are decremented as follows:
1. Q goes to 0
2. S goes to 1
3. R goes to 0
4. S goes to 0
Since all reference counts now equal 0, all nodes are deleted.
When you apply an action to a node, the action automatically creates a path that references the node. When the action finishes, it automatically removes the path, and thus decrements the node's reference count. Here again, if the node originally has a reference count of 0, it is deleted when the action finishes.
node to its original state (that is, reference count equals 0, but it still exists). For example:
// Create a sphere of a certain radius and returns its bounding // box. NOTE: BUGGY VERSION; provided for discussion only! SoSphere *makeSphere(float radius, SbBox3f &box) { sphere = new SoSphere; // reference count of 0 sphere->radius.setValue(radius); ba = new SoGetBoundingBoxAction; ba->apply(sphere); // does a ref/unref box = ba->getBoundingBox(); return sphere; // ERROR! returning node that // was deleted when ref count // went back to zero! }
In this example, the sphere node is referenced and unreferenced by SoGetBoundingBoxAction SoGetBoundingBoxAction SoGetBoundingBoxAction . When unreferenced, the sphere's reference count goes to 0, and it is deleted. The sphere needs to be referenced before the action is applied.
// Create a sphere of a certain radius and returns its bounding // box. NOTE: CORRECT VERSION SoSphere *makeSphere(float radius, SbBox3f &box) { sphere = new SoSphere; // reference count of 0 sphere->ref(); // we want it to stay around sphere->radius.setValue(radius); ba = new SoGetBoundingBoxAction; ba->apply(sphere); // does a ref/unref box = ba->getBoundingBox(); sphere->unrefNoDelete(); // ref count goes to zero, // but sphere stays around return sphere; // returns sphere with ref // count of zero }
Table 3.1, “References and Deletion ” summarizes the occurrences that increment and decrement reference counts of nodes and engines. Note that connecting an engine to a field in a node does not increment the node's reference count. (Engines are discussed in Chapter 15, Engines.)
Increments Reference Count by 1 |
Decrements Reference Count by 1 |
---|---|
Adding a node as a child of another node increments child's reference count |
Removing a node as a child of another node |
Adding a node to a path |
Removing a node from a path |
Applying an action to a node or path increments reference count of all nodes that are traversed |
When traversal for the action finishes, all nodes that were traversed are unreferenced |
Adding a node to an SoNodeList node |
Removing a node from an SoNodeList |
Setting an SoSFNode or SoMFNode value to point to a node |
Changing an SoSFNode or SoMFNode value to point to a different node or to NULL, or deleting the value |
Connecting an output of an engine to a field in a node or engine increments the engine's reference count |
Disconnecting an engine's output from the field decrements the engine's reference count |