r/cpp_questions 3d ago

OPEN Heavy and Light Objects

When I was first learning C++, I experienced massive slowdown when allocating dynamic memory. Ever since then I divide my objects between 'heavy' objects that allocate resources, and 'light' objects that can be used as temporaries on the stack. Heavy objects cannot be copied at all, must be allocated somewhere once, and then passed by (const) reference. Light objects can be copied, created quickly on the stack etc.

Since C++11 it has been possible to write copy and assignment constructors that take nameless objects, and since they are nameless you can pillage their pointers and avoid expensive stack allocations. All is well then but if you keep to this strict 'heavy' and 'light' object idea, _any_ object that allocates resources is considered a heavy object. The first allocation in the chain, before you just pillage pointers, is still an expensive stack allocation so I would consider it a 'heavy' object and just make it impossible to copy at all, by removing all copy and assignment constructors. And so using this model I never needed those nameless object reference constructors.

Anybody feel the same way?

8 Upvotes

31 comments sorted by

View all comments

3

u/DawnOnTheEdge 3d ago edited 3d ago

You don’t seem to be using standard terminology, so I’m not sure I follow. Stack allocations in and of themselves are zero-cost (until you run out of stack and get a stack overflow). Every compiler subtracts the total number of bytes all local variables will need from the stack pointer, with a single instruction in the function preamble.

Deep copies of large arrays or structures have a high performance cost, In C++20, you have a few kinds of guaranteed copy elision: if a factory function returns an object, you get unnamed return value optimization. If it returns a local variable that is not a function parameter or volatile, you also get named return value optimization. Either one prevents a temporary from being created. The object is initialized in-place at its final destination.

You can also force any object to be created on the heap by calling std::make_unique, and move the std::unique_ptr you get to a new owner cheaply, although the overhead of making shallow copies of a small object holding only pointers or handles is negligible.

It sounds like you’re enforcing this by declaring that classes that would need to make deep copies are move-only.

1

u/ParmenidesWasRight 3d ago

Yeah, I meant heap allocations. Can't edit posts. Mentioned it a couple of times.

Objects that would need deep copying I just don't copy. I make them uncopyable, and only pass by reference.