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 <WinSock2.h>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <iostream>
#include <stlsoft/smartptr/scoped_handle.hpp>

// 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<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<SOCKET> cleanupSocket(socketDesc, ::closesocket, INVALID_SOCKET); // ソケットが確実に閉じられるようにする。

            void *mem = std::malloc(10000);
            if (!mem) {
                throw std::bad_alloc();
            }
            else {
                stlsoft::scoped_handle<void*> 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<DWORD>(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();
    }
}