I usually don't call destructors explicitly. But I'm designing TCP server class, which looks something like this:
class Server {
public:
Server() {
try {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
throw std::runtime_error("WSAStartup function failed.");
...
if ((m_scListener = socket(pAddr->ai_family, pAddr->ai_socktype, pAddr->ai_protocol)) == INVALID_SOCKET)
throw std::runtime_error("'socket' function failed.");
...
}
catch (std::exception& ex) {
this->~Server();
throw;
}
}
~Server() {
if (m_scListener != INVALID_SOCKET) {
closesocket(m_scListener);
m_scListener = INVALID_SOCKET;
}
WSACleanup();
}
private:
SOCKET m_scListener = INVALID_SOCKET;
}
Is the code above considered as bad practice or design? What's recommended way of designing this? I wrote this way, because constructors can't return NULL. Should I make constructor private, and write static method that creates an instance of the Server class?
===== U P D A T E =====
OK, summarizing the answers, I came to this conclusion:
Explicitly calling a destructor is generally a bad idea, even if it works as intended, this is something unusual, and other C++ programmers who will be dealing with your code may get confused with this approach. So it's best to avoid explicitly calling destructors.
Breaking my original RAII class into micro RAII classes looks like a good solution. But I'm afraid that there are too many API calls in my real code that need for clean-ups (closesocket, CloseHandle, DeleteCriticalSection, etc...). Some of them are called only once and are never reused, and moving all of them into separate RAII classes seems too fanatical to me. This will also increase my code.
In my opinion the most helpful answer is from M.M:
A better solution would be to keep the initialization code in the constructor, and call the cleanup function before throwing out.
Following the M.M's advice I rewrote my code this way:
class Server {
public:
Server() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
ThrowError("WSAStartup function failed.", true);
...
if ((m_scListener = socket(pAddr->ai_family, pAddr->ai_socktype, pAddr->ai_protocol)) == INVALID_SOCKET)
ThrowError("'socket' function failed.", true);
...
}
~Server() { CleanUp(); }
private:
SOCKET m_scListener = INVALID_SOCKET;
void ThrowError(const char* error, bool cleanUp) {
if (cleanUp)
CleanUp();
throw std::runtime_error(error);
}
void CleanUp() {
if (m_scListener != INVALID_SOCKET) {
closesocket(m_scListener);
m_scListener = INVALID_SOCKET;
}
WSACleanup();
}
};
I believe this design follows the RAII pattern, but only one class instead of 3-4 micro RAII classes.