As others have said, if the Foo instance's non-heap contents aren't so large that the Foo instance itself needs to be allocated on the heap, it is better to just use the stack:
Foo foo;
foo.printValue();
However, if it does need to be allocated on the heap for some reason, then it's dangerous to hold the only reference to it in an ordinary pointer, since if an exception gets thrown by any code, it will never be deallocated. In fact, most modern C++ guidelines advise not using ordinary pointers to own data for this reason:
Foo * fooPtr = new Foo();
doSomething(); // If an exception is thrown here, the Foo never gets deallocated!
Foo& foo = *fooPtr;
foo.printValue(); // If printValue throws an exception, the Foo never gets deallocated!
delete foo;
If you aren't familiar with this sort of problem, I suggest googling RAII ("Resource Acquisition Is Initialization") which is an crucial concept to understand when programming in C++.
An easy way to implement RAII in a case like this is through use of a smart pointer (std::shared_ptr or std::unique_ptr).
An extra reference variable can still be helpful to avoid having to use the arrow operator to call functions on the smartpointer. I disagree with some other answerers who don't see value in also binding a local reference to a value already held in a local pointer. I prefer to use references whenever possible, since when I use a reference, I can be sure that the reference isn't null (references should always refer to actual objects), while when I use a pointer (even a smart pointer) I must always be careful that my code correctly handles the case where the pointer is null. When the pointer initialization occurs close to the pointer's use, this may not be a big deal, but when they are separated, it can become hard to trace through the code to be sure the pointer can't be null. A reference makes this self-documenting.
I think that it is often useful to first ensure that a pointer or smart pointer value can't be null, and then to bind a local reference to the pointed-to value, since I can then use the reference freely without having to worry at each use about the possibility of it being null:
std::unique_ptr<Foo> fooPtr = std::make_unique<Foo>(/* Foo constructor args go here */);
doSomething(); // Now if an exception thrown here, Foo gets deallocated.
Foo& foo = *fooPtr; // We know the pointer is not null here (it just
// got returned from make_unique which
// didn't throw a bad_alloc exception) so it's
// safe to bind a reference to it here.
// Also, this reference has lifetime less than the
// smart pointer, so will never outlive it.
// .. many lines of code later ..
foo.printValue(); // No need to worry about null here.
// If exception thrown here, the unwinding of the stack
// causes fooPtr to deallocate Foo.
// No need to call delete here.
// fooPtr will automatically deallocate Foo when it goes out of scope.