You're contradicting yourself.
I tried to catch the exception in the catch block. But after the catch block is run
If the catch block is run, you succeeded catching the exception from the initializer list. You're just surprised by what happened next.
First, let's think about what happens when a constructor throws: the object is not constructed. Right? the constructor never finished setting it up, so you can't use it for anything.
int main() {
Cla obj; // member subobject constructor throw, but we caught it!
obj.print(); // but we still can't use this here, because the constructor never completed
}
So it doesn't really make sense to let you swallow the exception here. You can't handle it, because you can't go back and re-try constructing your member and base-class subobjects. If you don't have a properly-constructed object, the only way C++ has of dealing with that is to unwind the block scope in which that object would otherwise be assumed to be ... well, a real object.
Hence, per the documentation:
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
... and equivalently in the standard (draft)
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.
Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function (see [stmt.return])