Fungsi dan kelas friend

Fungsi dan kelas friend serta kapan pemakaiannya beralasan.

Daftar isi

Topik ini membahas fungsi friend dan kelas friend, yaitu mekanisme yang memberi kode di luar kelas izin mengakses member private dan protected. Materinya mencakup cara mendeklarasikan keduanya, arah dan batas relasi friendship yang terbentuk, serta situasi penggunaan yang beralasan dan yang sebaiknya dihindari. Setelah membacanya, akses khusus dapat diberikan secara terkendali tanpa meruntuhkan enkapsulasi yang sudah dibangun.

Fungsi friend#

Fungsi friend adalah fungsi biasa di luar kelas yang memperoleh izin mengakses member private kelas tersebut. Izinnya dinyatakan dari dalam kelas dengan keyword friend di depan prototipe fungsinya, sedangkan definisi fungsinya ditulis seperti fungsi bebas lainnya. Fungsi friend bukan member, sehingga tidak memiliki this dan tidak dipanggil lewat objek, melainkan dipanggil seperti fungsi biasa. Kelas Kotak berikut membuka akses kepada dua fungsi yang membaca ukuran privatnya.

cpp kotak-friend.cpp
#include <iostream>

class Kotak {
private:
    double panjang;
    double lebar;

public:
    Kotak(double p, double l) : panjang(p), lebar(l) {
    }

    friend void tampilkan(const Kotak& k);
    friend bool lebihLuas(const Kotak& a, const Kotak& b);
};

void tampilkan(const Kotak& k) {
    std::cout << k.panjang << " x " << k.lebar << "\n";
}

bool lebihLuas(const Kotak& a, const Kotak& b) {
    return a.panjang * a.lebar > b.panjang * b.lebar;
}

int main() {
    Kotak a(4.0, 3.0);
    Kotak b(5.0, 2.0);

    tampilkan(a);
    tampilkan(b);

    if (lebihLuas(a, b)) {
        std::cout << "Kotak a lebih luas\n";
    } else {
        std::cout << "Kotak b lebih luas\n";
    }
    return 0;
}
text Keluaran
4 x 3
5 x 2
Kotak a lebih luas

Kedua fungsi tersebut menulis k.panjang dan k.lebar secara langsung, yang tanpa izin friend akan ditolak compiler karena kedua member itu bersifat private. Deklarasi friend di dalam kelas hanya menjadi pemberian izin, bukan definisi, sehingga kedua fungsi tetap berada di luar lingkup kelas. Pola yang paling lazim dari fungsi friend adalah operator pencetakan operator<< yang memerlukan akses ke data private tetapi tidak dapat menjadi member karena operand kirinya berupa stream. Pola itu dibahas lengkap pada bahasan operator overloading.

Kelas friend#

Deklarasi friend class Nama; di dalam kelas memberi seluruh member function pada kelas Nama akses ke member private kelas pemberi izin. Izin ini menyeluruh, sehingga kelas friend cocok untuk pasangan kelas yang memang bekerja pada representasi data yang sama secara erat. Pasangan container beserta iteratornya pada pustaka standar merupakan contoh yang ditemui secara nyata. Program berikut memuat kelas Kas yang menyimpan saldo secara private dan kelas Bendahara yang diberi izin memeriksanya.

cpp kas-bendahara.cpp
#include <iostream>

class Kas {
private:
    double saldo;

public:
    explicit Kas(double saldoAwal) : saldo(saldoAwal) {
    }

    friend class Bendahara;
};

class Bendahara {
public:
    void periksa(const Kas& k) const {
        std::cout << "Saldo kas: " << k.saldo << "\n";
    }
};

int main() {
    Kas kasKelas(250000);
    Bendahara b;

    b.periksa(kasKelas);
    return 0;
}
text Keluaran
Saldo kas: 250000

Member function periksa membaca k.saldo karena Kas menyatakan Bendahara sebagai friend. Arah pemberian izin perlu dicermati, yaitu Kas yang membukakan izinnya, sedangkan Bendahara tidak perlu menyatakan apa pun dan tetap merupakan kelas biasa. Izin juga berlaku untuk seluruh member function Bendahara, baik yang ada sekarang maupun yang ditambahkan kemudian, sehingga pertumbuhan kelas friend ikut memperluas aksesnya. Perhatikan pula urutan definisinya, yaitu Kas didefinisikan lebih dahulu supaya badan periksa mengenal tipe Kas secara lengkap.

Sifat relasi friend#

Istilah friendship menunjuk relasi pemberian akses ini, dan relasinya memiliki tiga batas yang menentukan seberapa jauh izin itu menyebar. Ketiganya bersifat tegas menurut aturan bahasa, bukan sekadar konvensi, sehingga perlu diingat sebelum merancang kelas yang saling memberi akses.

  • Tidak resiprokal, karena Bendahara yang menjadi friend Kas tidak otomatis menjadikan Kas sebagai friend Bendahara.
  • Tidak ditransmisikan, karena kelas yang dijadikan friend oleh sebuah friend tidak ikut memperoleh akses apa pun.
  • Tidak diwariskan, karena kelas turunan dari friend tidak mewarisi izin tersebut dan harus dinyatakan friend masing-masing.

Letak deklarasi friend di wilayah private maupun public juga sama saja, karena keyword friend sendirian yang menentukan izinnya. Batas-batas di atas menjadikan friend sebagai izin yang dapat diaudit dari satu tempat, yaitu definisi kelas pemberi izin. Seluruh daftar pihak yang boleh menyentuh data private terbaca dari definisi kelas itu tanpa perlu menyisir berkas lain.

Kapan penggunaannya beralasan#

Penggunaan friend beralasan apabila sebuah fungsi memerlukan akses ke data private tetapi secara logika bukan bagian dari antarmuka objek. Operator biner seperti perbandingan dua objek Kotak adalah contohnya. Kedua operandnya setara, sehingga menjadikannya member justru membuat bentuk pemanggilannya timpang. Operator stream operator<< masuk kategori yang sama karena operand kirinya berupa std::ostream. Kasus lain yang wajar adalah pasangan kelas yang erat, misalnya sebuah container dan iteratornya, yang memang dirancang untuk berbagi representasi internal.

Sisanya perlu dipertimbangkan ulang. Memberi akses friend semata-mata untuk menghindari penulisan fungsi pembaca publik melemahkan enkapsulasi yang justru menjadi tujuan kelas, dan daftar friend yang panjang memperlebar kode yang bergantung pada detail internal. Pengujian internal kelas kadang juga dijadikan alasan, tetapi pilihan ini sebaiknya disimpan untuk kasus yang benar-benar memerlukannya. Apabila antarmuka publik dapat diperluas dengan member function yang menjaga aturan kelas, pilihan itu lebih diutamakan. Friend digunakan sebagai pengecualian yang tertulis jelas, bukan sebagai jalan pintas yang menyebar.

Kesalahan umum#

Tiga kekeliruan berikut paling sering muncul saat menggunakan friend.

  • Memanggil fungsi friend lewat objek, misalnya a.tampilkan(k). Fungsi itu bukan member, sehingga compiler menolaknya. Perbaikannya adalah memanggil tampilkan(k) seperti fungsi biasa.
  • Mengira deklarasi friend sekaligus membuat fungsi itu tersedia untuk semua kode. Izin yang diberikan hanya berlaku bagi fungsi atau kelas yang disebut namanya, dan definisinya tetap harus ada di tempat yang dapat dilihat pemanggilnya.
  • Mengharapkan akses friend diteruskan ke kelas turunan dari kelas friend. Izin itu tidak diwariskan, sehingga kelas turunan yang membutuhkan akses harus dinyatakan friend secara eksplisit.

Ringkasan#

  • Fungsi friend dan kelas friend memperoleh izin mengakses member private dan protected melalui deklarasi friend di dalam kelas pemilik data.
  • Fungsi friend bukan member, sehingga dipanggil seperti fungsi biasa dan tidak memiliki this.
  • Relasi friend tidak resiprokal, tidak ditransmisikan, dan tidak diwariskan.
  • Penggunaan yang beralasan mencakup operator biner yang simetris, operator stream, dan pasangan kelas yang erat, sedangkan sisanya diselesaikan lewat antarmuka publik.

Referensi#