This idiom is often used in library interfaces, mostly in C, but also in
C++. For example, the main "handle" type SSL in openssl 1.1 is defined as:
typedef struct ssl_st SSL;
without definition of ssl_st. The public functions are defined in terms
of SSL*. This is in contrast to openssl 1.0 where the struct definitions
were public; the change was specifically done to hide implementation
details - see "
https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes".
Another example are Python C bindings (CPython), where most of the
interfaces are defined in terms of "base class" pointer PyObject*, but
in reality there is no PyObject object, all objects are of some
"derived" class like PyLongObject, etc. So the implementation code
contains a lot of casting which are effectively reinterpret_cast-s
because there is no inheritance in C and all those structs are
technically unrelated.
In C++ the dreadful reinterpret_cast is actually not needed if things
are done properly, static_cast is guaranteed to do the correct thing in
derived class hierarchies.
Of course, using such raw pointers more suits a C-style interface. BTW,
it is a great idea to provide a C-style interface for a C++ library,
this allows for its much wider use and avoids some technical
complications related to the lack of standardized ABI in C++.
However, in pure C++ one typically expects to have automatic resource
release in some destructor where needed, so the raw pointer should be
packed into some small "handle" class, either std::shared_ptr or
something more elaborate. In this way we arrive to the pimpl idiom,
which is indeed often used in C++ for hiding implementation details.