ValueTree& vs. ValueTree* vs. ValueTree (ValueTree&)

When maintaining references to a ValueTree node (e.g in a Component / ComponentAttachment to keep track of the node to read/write from/to), is there ever any (e.g. performance) advantage in using ValueTree& or ValueTree* ?

Or should I always be using

//Creates a reference to another ValueTree.
ValueTree::ValueTree (const ValueTree&) noexcept

and storing the references as ValueTree objects?

From the docs:

Create ValueTree objects on the stack, and don’t be afraid to copy them around, as they’re simply a lightweight reference to a shared data container.
Creating a copy of another ValueTree simply creates a new reference to the same underlying object - to make a separate, deep copy of a tree you should explicitly call createCopy().

So ValueTree* makes no sense.

Here you find the difference between the copy constructor and the assignment operator:

HTH

1 Like

Thank you @daniel :thumbsup:

I agree with this, but to help with a comprehensive answer to the OP’s question, I’d add that there are cases where ValueTree& does make sense.

I had been following that advice from the docs that you quoted, to:

Create ValueTree objects on the stack, and don’t be afraid to copy them around…

But then I got tripped up in a case where I had two classes, each with a ValueTree member, where I had set them to share data via a simple assignment by value (e.g. valueTreeA = valueTreeB). Then I wanted to change the ValueTree reference from within one of the classes to a different ValueTree –– and (with fuzzy thinking) assumed that the ValueTree in the other class would follow that changed reference too. Nope.

For that, you need one of the classes’ ValueTree members to instead be a ValueTree&, and then of course set that reference in the initializer list.

Indeed, if two ValueTrees show the same tree, an assignment will not propagate to the other one.

But bear in mind, if one is a reference, it is bound to the lifetime of the referenced ValueTree. If the referenced tree goes out of scope, your reference will become dangling.

If you want to replace the data of both ValueTrees, you can use copyPropertiesAndChildren() instead.

I came across this thread because I too have wondered the difference between ValueTree and ValueTree& practically speaking (this thread answered my question) However, and it’s possible I’m missing something here, but I will occasionally use a shared_ptr to reference a ValueTree in some object where I want to receive callbacks from said value tree but am unable or don’t want to initialize a ValueTree& at the construction of the object.

So although this is not the same as a raw ptr I became intrigued by the motivations behind your comment.

Care to elaborate? I realize this comment was made years ago haha but I wanted to make sure I wasn’t unintentionally doing something unsafe or potentially risky or maybe there’s a better way to achieve the same results.

Sure. If you look at the ValueTree, it is a lightweight wrapper around a SharedObject, which inherits ReferenceCountedObject. So internally the ValueTree is already something very similar to a std::shared_ptr. You can copy a ValueTree at no cost, the copy will reference the same data.

The only moment you need a ValueTree& is to add Listeners. The ListenerList is not part of the SharedObject, so when copying the ValueTree the listeners are not connected to the new copy.

In case object A has a ValueTree and object B wants to register a Listener to that tree, object A needs to expose the ValueTree as reference. But you could as well hold your own ValueTree in B referencing the same SharedObject (created by copying the ValueTree from A) and attach your Listeners there. This makes it much safer in terms of lifetime (when the Listener is at the same time the owner of the object it listens to, there is no chance the pointer in the ListenerList becomes dangling).

7 Likes