Inheriting from std::runtime_error is the most common approach because it handles string storage internally:
CUSTOM EXCEPTION HIERARCHY:
┌────────────────────────┐
│ std::runtime_error │
└───────────┬────────────┘
│
┌───────────┴────────────┐
│ NetworkTimeoutError │ ──► provides errorCode + what()
└────────────────────────┘