Menu
CoddyTech

RAII sebagai Pola

Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 102 dari 104.

RAII (Resource Acquisition Is Initialization) bukan sekadar idiom C++, tetapi pola desain yang ampuh dan mengaitkan pengelolaan sumber daya dengan masa hidup object. Kamu sudah melihat RAII pada smart pointer, tetapi pola ini berlaku untuk sumber daya apa pun: file handle, koneksi jaringan, mutex, atau transaksi database.

Gagasan utamanya sederhana: dapatkan sumber daya di constructor, lepaskan di destructor. Karena C++ menjamin destructor dijalankan saat object keluar dari cakupan, pembersihan terjadi secara otomatis, bahkan saat exception terjadi:

#include <iostream>
#include <fstream>

class FileGuard {
    std::ofstream file;
public:
    FileGuard(const std::string& filename) : file(filename) {
        if (!file.is_open()) {
            std::cout << "Failed to open file\n";
        }
    }
    
    void write(const std::string& text) {
        if (file.is_open()) file << text;
    }
    
    ~FileGuard() {
        if (file.is_open()) {
            file.close();
            std::cout << "File closed automatically\n";
        }
    }
};

int main() {
    {
        FileGuard guard("output.txt");
        guard.write("Hello RAII");
    }  // Destructor dipanggil di sini, file ditutup
    
    std::cout << "After scope\n";
}

RAII sangat berguna saat mengelola lock dalam kode multithread. std::lock_guard dari library standar mengikuti pola ini: objek ini memperoleh mutex saat konstruksi dan melepasnya saat destruksi, sehingga mencegah deadlock akibat lupa melepas lock.

Saat mengimplementasikan class RAII, ingat untuk menghapus atau mengimplementasikan operasi penyalinan/pemindahan dengan benar (Rule of Five) agar tidak terjadi duplikasi resource atau resource dilepas dua kali. RAII mengubah pengelolaan resource manual yang rentan kesalahan menjadi pembersihan otomatis yang aman.

challenge icon

Tantangan

Mudah

Mari buat Connection Pool Manager menggunakan RAII untuk mengelola koneksi database dengan aman. Dalam aplikasi nyata, koneksi database adalah sumber daya yang mahal dan harus diperoleh serta dilepaskan dengan benar. Kamu akan membuat pembungkus RAII yang menjamin koneksi selalu dikembalikan ke pool, bahkan jika terjadi exception atau jalur kode menjadi rumit.

Kamu akan mengatur kodemu dalam tiga file:

  • ConnectionPool.h: Buat pool koneksi sederhana yang mengelola jumlah koneksi terbatas.

    Class ConnectionPool harus melacak jumlah koneksi yang tersedia, dimulai dari kapasitas yang diberikan ke constructor. Implementasikan:

    • acquire(): jika koneksi tersedia, kurangi jumlahnya dan tampilkan Connection acquired (X available), dengan X sebagai jumlah yang tersisa; kembalikan true jika berhasil, atau false jika tidak ada koneksi yang tersedia
    • release(): tambah jumlah koneksi yang tersedia dan tampilkan Connection released (X available)
    • available(): mengembalikan jumlah koneksi yang tersedia saat ini
  • ConnectionGuard.h: Buat pembungkus RAII yang mengelola satu koneksi dengan aman.

    Class ConnectionGuard menerapkan pola RAII. Class ini harus:

    • Menerima referensi ke ConnectionPool di constructornya dan mencoba memperoleh koneksi
    • Menyimpan informasi apakah upaya memperoleh koneksi berhasil
    • Menyediakan method isConnected() untuk memeriksa apakah guard memegang koneksi yang valid
    • Secara otomatis mengembalikan koneksi ke pool di destructor, hanya jika koneksi berhasil diperoleh
    • Menghapus copy constructor dan copy assignment untuk mencegah duplikasi sumber daya (pertimbangan Rule of Five)

    Saat destructor berjalan, jika koneksi sedang dipegang, tampilkan Guard releasing connection sebelum memanggil release pada pool.

  • main.cpp: Tunjukkan pembersihan otomatis RAII melalui scope.

    Baca dua input:

    1. Kapasitas pool (integer)
    2. Jumlah koneksi yang diminta (integer)

    Buat ConnectionPool dengan kapasitas yang diberikan. Kemudian, di dalam scope bertingkat (menggunakan kurung kurawal), buat sejumlah object ConnectionGuard yang diminta dan simpan dalam vector. Untuk setiap guard, tampilkan apakah koneksi berhasil diperoleh:

    • Jika koneksi berhasil: Guard N: Connected
    • Jika koneksi tidak berhasil: Guard N: Failed to connect

    dengan N dimulai dari 1

    Setelah scope berakhir dan guard dihancurkan, tampilkan After scope: X connections available untuk menunjukkan kondisi akhir pool.

Contohnya, dengan input 2 dan 3:

Connection acquired (1 available)
Guard 1: Connected
Connection acquired (0 available)
Guard 2: Connected
Guard 3: Failed to connect
Guard releasing connection
Connection released (1 available)
Guard releasing connection
Connection released (2 available)
After scope: 2 connections available

Dengan input 3 dan 2:

Connection acquired (2 available)
Guard 1: Connected
Connection acquired (1 available)
Guard 2: Connected
Guard releasing connection
Connection released (2 available)
Guard releasing connection
Connection released (3 available)
After scope: 3 connections available

Perhatikan bahwa koneksi dilepaskan secara otomatis saat guard keluar dari scope. Kamu tidak pernah memanggil release secara eksplisit di kodemu pada main. Destructor berjalan dalam urutan terbalik dari konstruksi, guard terakhir dihancurkan lebih dulu, dan setiap koneksi yang berhasil diperoleh dijamin akan dikembalikan. Inilah keunggulan RAII: pembersihan sumber daya berlangsung secara otomatis dan andal, apa pun cara scope berakhir.

Coba sendiri

#include <iostream>
#include <vector>
#include "ConnectionPool.h"
#include "ConnectionGuard.h"

using namespace std;

int main() {
    // Baca input
    int capacity;
    int numConnections;
    cin >> capacity;
    cin >> numConnections;
    
    // TODO: Buat ConnectionPool dengan kapasitas yang diberikan
    
    // TODO: Buat cakupan bertingkat menggunakan kurung kurawal
    {
        // TODO: Buat vector untuk menyimpan object ConnectionGuard
        // Petunjuk: Kamu perlu menggunakan pointer atau smart pointer karena ConnectionGuard
        // memiliki copy constructor yang dihapus
        
        // TODO: Gunakan perulangan untuk membuat numConnections guard
        // Untuk setiap guard, cetak salah satu dari:
        // "Guard N: Connected" atau "Guard N: Failed to connect"
        // dengan N dimulai dari 1
        
    }
    // Guard dihancurkan di sini saat cakupan berakhir
    
    // TODO: Cetak "After scope: X connections available"
    
    return 0;
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Object

Latihan sendiri: Compiler C++ online