There are all sorts of issues with pointers (and not just in C++) - which are inherent to their use. They can point anywhere! They're mutable! They can be null! It's difficult/impossible to ensure they hold a valid value!
Common wisdom is to avoid excessive use of pointers when you don't _really_ need - even smart pointers.
Use references, especially as function parameters,
* Returning values in modern C++ typically does _not_ involve any copying.
* If you want to indicate the possibility that your value is uninitialized/invalid - use std::optional<T>, which can hold either an actual T value or an std::nullopt (being in an "empty" or "value-less" state).
* If your data is in some buffer (or span, or vector etc.), you can use offsets into that buffer.
* Many uses of pointers are due to the "need" to utilize polymorphism: myobj->foo() . Well, typically, you know the real type at compile-time, and can write a freestanding foo() function, which is polymorphic via overloading, or being templated over its parameter's type.
* And speaking of virtual methods and class hierarchies, you can often make do with a template parameter instead of a choice of subclass; or with an std::variant<Foo, Bar>
There are all sorts of issues with pointers (and not just in C++) - which are inherent to their use. They can point anywhere! They're mutable! They can be null! It's difficult/impossible to ensure they hold a valid value!
Common wisdom is to avoid excessive use of pointers when you don't _really_ need - even smart pointers.
Consider this fine presentation for example:
"Don't use fking pointers" https://klmr.me/slides/modern-cpp/#1
Use references, especially as function parameters,
* Returning values in modern C++ typically does _not_ involve any copying.
* If you want to indicate the possibility that your value is uninitialized/invalid - use std::optional<T>, which can hold either an actual T value or an std::nullopt (being in an "empty" or "value-less" state).
* If your data is in some buffer (or span, or vector etc.), you can use offsets into that buffer.
* Many uses of pointers are due to the "need" to utilize polymorphism: myobj->foo() . Well, typically, you know the real type at compile-time, and can write a freestanding foo() function, which is polymorphic via overloading, or being templated over its parameter's type.
* And speaking of virtual methods and class hierarchies, you can often make do with a template parameter instead of a choice of subclass; or with an std::variant<Foo, Bar>