In C++, user defined types, by design, will have default behavior as primitive types do. This uniformity has many advantages, but it puts burden on the developer to decide how parameters are passed in and out of functions.
There are some optimizations which will be used to avoid unnecessary copying. Basically, there are two main categories:
- Copy elision
- Move semantics
Their understanding is crucial for your efficiency. Move semantics (introduced in C++0x, now in C++11) is maybe easier to understand because it has explicit language syntax and it is pretty much clear that every temporary (rvalue) and explicitly moved lvalue is a candidate for move construction.
On the other hand, copy elision is more subtle, it’s not mandatory by Standard and it’s compiler dependent optimization.