The C++ syntax is context-sensitive, so the meaning of * depends on the surrounding code. In declarations, it generally comes after the type, i.e. char* x declares a pointer to a char. In an expression, *x dereferences the variable x (which needs to be a pointer) and so *x is the original object pointed at by x (which may itself be a pointer, if x was a pointer to a pointer, for instance). You can then access its members, if it has any, via (*x).blah.
The operator -> is indeed just a handy shortcut for referencing a pointer and accessing one of its members. No doubt the language designers thought it would be convenient because of less brackets, and also more readable. And also it is less prone to precedence errors, i.e. typing *a.b instead of (*a).b, whereas there is no such ambiguity with a->b.
So -> is not strictly necessary, but it is useful nonetheless. You could also argue * is not necessary since you can dereference a pointer by treating it as an array, i.e. *x is the same as x[0]. Or the other way around, by accessing array elements through pointer offsets. Basically, there are many ways to achieve the same thing in C/C++. Did you know 5[x] is valid syntax and absolutely equivalent to x[5] if x is a pointer/array?