"shallow" versus "deep" copy is less meaningful in C++ than it is in C or Java.
To illustrate this, I've changed your Foo class from three ints to an int, an int*, and a vector<int>:
#include <iostream>
#include <vector>
class Foo {
public:
  int a;
  int *b;
  std::vector<int> c;
};
using namespace std;
int main() {
  Foo f1, f2;
  f1.a = 42;
  f1.b = new int(42);
  f1.c.push_back(42);
  f2 = f1;
  cout << "f1.b: " << f1.b << " &f1.c[0]: " << &f1.c[0] << endl;
  cout << "f2.b: " << f2.b << " &f2.c[0]: " << &f2.c[0] << endl;
}
When this program is run, it yields the following output:
f1.b: 0x100100080 &f1.c[0]: 0x100100090
f2.b: 0x100100080 &f2.c[0]: 0x1001000a0
The int is boring, so I've left it out.  But look at the difference between the int* and the vector<int>: the int* is the same in f1 and f2; it's what you would call a "shallow copy".  The vector<int> however is different between f1 and f2; it's what you would call a "deep copy".
What's actually happened here is that the default operator = in C++ behaves as if the operator = for all of its members were called in order.  The operator = for ints, int*s, and other primitive types is just a byte-wise shallow copy.  The operator = for vector<T> performs a deep copy.
So I would say the answer to the question is, No, the default assignment operator in C++ does not perform a shallow copy.  But it also doesn't perform a deep copy.  The default assignment operator in C++ recursively applies the assignment operators of the class's members.