出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2021/07/14 06:48 UTC 版)
「RAII」の記事における「広義のスマートポインタの活用」の解説
スマートポインタと呼ばれる類のクラスを使い、RAIIを任意のリソース管理APIへ適用することも可能である。 なお、この節では広い意味でスマートポインタという言葉を使っている。一般的にはメモリに特化したものをスマートポインタと言う。 例えば、stlsoft::scoped_handleは、(voidを含む)任意の型の解放関数を受け付け、また0でない広義の「ヌル」値(無効値)も受け付ける(Windowsのように複数の呼出規約が用いられる環境では、どんなものでも受け付ける)。 以下はWindows APIのファイル入出力関数およびWinsock API関数のリソースをRAIIでラップした例である。CreateFile()関数が返す無効値INVALID_HANDLE_VALUEおよびWSASocket()関数が返す無効値INVALID_SOCKETは、Windows SDK 8.1ではそれぞれ以下のように定義されている。 #define INVALID_HANDLE_VALUE ((HANDLE)((LONG_PTR)-1)) #define INVALID_SOCKET (SOCKET)(~0) RAIIは特に複数のリソースを同時に管理する場合に効果を発揮する。少なくともtry-catch節がいくつも現れて混乱する事態からは逃れられる。 #include #include #include #include #include #include #include // 3つの資源を同時に使う。void testScopedHandle() { // ファイルを開く。 // CreateFile() は失敗した場合 INVALID_HANDLE_VALUE を返す。ただし NULL もまた HANDLE としては一般的に無効値。 HANDLE hFile = ::CreateFileW(L"test.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == NULL || hFile == INVALID_HANDLE_VALUE) { throw std::runtime_error("Failed to create file handle!"); } else { stlsoft::scoped_handle cleanupFile(hFile, ::CloseHandle, INVALID_HANDLE_VALUE); // ファイルが確実に閉じられるようにする。 // TCP ソケットを作成。 // BSD ソケット API における socket() 関数の戻り値は int で、異常値は負数 (-1) となっており、0 は正常値のひとつ。 // Winsock もそれを踏襲している。 SOCKET socketDesc = ::WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); if (socketDesc == INVALID_SOCKET) { throw std::runtime_error("Failed to create socket descriptor!"); } else { stlsoft::scoped_handle cleanupSocket(socketDesc, ::closesocket, INVALID_SOCKET); // ソケットが確実に閉じられるようにする。 void *mem = std::malloc(10000); if (!mem) { throw std::bad_alloc(); } else { stlsoft::scoped_handle cleanupMem(mem, std::free); // メモリが確実に解放されるようにする。 // ここでメモリとソケットとファイルを使う。 const LARGE_INTEGER dummy = {}; if (!SetFilePointerEx(cleanupFile.get(), dummy, NULL, FILE_END)) { throw std::runtime_error("Failed to set file pointer to end!"); } const char* text = "Test line.\r\n"; const DWORD numOfBytesToWrite = static_cast(std::strlen(text)); DWORD numOfBytesWritten = 0; if (!::WriteFile(cleanupFile.get(), text, numOfBytesToWrite, &numOfBytesWritten, NULL) || numOfBytesWritten != numOfBytesToWrite) { throw std::runtime_error("Failed to write data on file!"); } // ... } // mem はここで解放される。 mem = NULL; // ソケットを自動的な管理から切り離す。 //SOCKET detachedVal = cleanupSocket.detach(); //assert(detachedVal == socketDesc); } // socketDesc を RAII から切り離した場合、ここでは閉じられない。 //const int ecode = ::closesocket(socketDesc); socketDesc = INVALID_SOCKET; // 早期に hFile の資源を返却することもできる。 //cleanupFile.close(); } // hFile の資源を早期に返却した場合、ここでは返却されない。 hFile = INVALID_HANDLE_VALUE;}int main() { WSADATA wsaData = {}; const int ecode = ::WSAStartup(MAKEWORD(2, 2), &wsaData); if (ecode == 0) { try { testScopedHandle(); } catch (const std::exception& ex) { std::cout << ex.what() << std::endl; } ::WSACleanup(); }}
※この「広義のスマートポインタの活用」の解説は、「RAII」の解説の一部です。
「広義のスマートポインタの活用」を含む「RAII」の記事については、「RAII」の概要を参照ください。