Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use one-element STL container as cache?

Tags:

c++

I have a class (let's call it Class1), where the objects of this class occasionally need an object of another class (let's call it Class2). Objects of Class2 are expensive to construct, so I want to construct it only if really needed, and then save it as cache for future use in the respective object of Class1.

One possibility would be to have a Class2* (or smart pointer) data member in Class1 which initially is the nullptr, but this creates the usual difficulties with memory management, copying objects of Class1 etc. Of course this is a manageable problem, but I want to prevent this if possible.

Why not use a STL container data member for Class1 like std::vector or std::unordered_map which are empty at the beginning and, if needed, the object of Class2 is saved (by constructing it with an emplace-like member function) as the only element in the container? I assume this has worse performance, but probably it's not that bad comparing to the resources needed to construct a Class2 object...

So, my question distilled:
Is it a good idea to use a one-element container as a data member for caching? If not, why not and what is a good alternative? If yes, which container would be best to use?

Thanks four your help and opinions!

Edit: To clarify what should happen if an object of Class1 is copied: Either the cached object should be copied into the new object, or the new cache should be empty, both is fine. What I don't want is to copy a pointer to the same object.

Edit 2: I should have mentioned already at the beginning that the code uses only C++11 features up to now and it would be good if it stayed like this. But I also appreciate all the comments mentioning C++14 and C++17 features since they are interesting and useful in general.

like image 374
tz39R Avatar asked Mar 01 '26 06:03

tz39R


2 Answers

You wouldn't typically use a container for this; though there's nothing wrong with a container only having one element, we generally use them because we want to manage a collection of more than one element, and their design revolves around that functionality.


Traditionally, then, not to gain performance so much as to signal intent, you'd want some kind of "optional" object, and in the olden days you'd implement that by new-ing an instance of your type only when you need it (and keeping a null pointer otherwise). Since C++11 you should use std::unique_ptr for that purpose instead, solving your "usual difficulties with memory management".

If you want copying your class to deep-copy the cache, simply put m_cache(other.m_cache ? std::make_unique<TheType>(*other.m_cache) : nullptr) in your copy constructor's init list!


In modern times (since C++17, and even before if you're happy with Boost), you can skip the dynamic allocation (and deep copy antics) and use std::optional for this instead, which is the current idiomatic solution.

You could always have implemented this with placement-new and a bool, actually, but it's nice to have a pre-packaged solution to do all that for us.

Note that, as a consequence of the non-dynamic allocation, this solution will always take up sizeof(TheType)+sizeof(bool) space, whether your cache is in use or not.

like image 188
Asteroids With Wings Avatar answered Mar 03 '26 20:03

Asteroids With Wings


You want to use optional type.

std::optional in C++17.

Before you might use boost::optional or other libraries implementing it.

Or implement your own version.

like image 29
Jarod42 Avatar answered Mar 03 '26 20:03

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!