Member function dan this

Metode, member function const, dan keyword mutable.

Daftar isi

Topik ini membahas member function secara lebih dalam, yaitu fungsi yang dideklarasikan di dalam kelas dan dipanggil melalui objek. Materinya mencakup parameter objek tersembunyi di balik setiap pemanggilan, kata kunci this, member function const untuk objek yang tidak boleh diubah, serta kata kunci mutable untuk member yang boleh berubah di dalam member function const. Setelah membacanya, pembaca dapat menulis member function dengan kontrak perubahan yang jelas.

Member function dan parameter objek tersembunyi#

Member function selalu dijalankan atas nama satu objek tertentu, dan objek itu ikut tersedia di dalam fungsinya tanpa perlu dikirim sebagai argumen. Pernyataan p.bacaX() pada kelas Titik secara efektif seperti memanggil fungsi biasa dengan p sebagai parameter tambahan. Parameter tersembunyi ini disebut implicit object parameter, dan nama this adalah cara kode di dalam member function merujuknya.

cpp Contoh: makna pemanggilan member function
// tidak-dijalankan (ilustrasi cara compiler melihat pemanggilan member function)
p.bacaX();
Titik::bacaX(&p);

Kedua baris di atas memiliki makna yang sama bagi compiler, yaitu member function bacaX dijalankan dengan alamat objek p sebagai argumen tambahan yang diterimanya tanpa ditulis. Di dalam badan member function, setiap penyebutan member seperti x membaca member dari objek yang dirujuk oleh parameter tersembunyi itu. Karena alasan inilah member function tidak dapat dipanggil tanpa objek. Pemanggilan Titik::bacaX() tanpa objek ditolak compiler, kecuali fungsinya bertipe member function static yang dibahas pada topik member static.

Kesadaran tentang parameter tersembunyi ini juga menjelaskan mengapa member function dapat membaca member private kelasnya secara bebas. Akses itu diberikan karena fungsi tersebut memang bagian dari kelas, sedangkan objek yang diprosesnya diserahkan melalui parameter tersembunyi pada saat pemanggilan.

Keyword this dan pengembalian *this#

Kata kunci this adalah pointer yang menyimpan alamat objek yang sedang diproses oleh member function, sehingga this->x dan x merujuk member yang sama. Pada umumnya kata ini tidak perlu ditulis, tetapi this menjadi penting ketika member function perlu mengembalikan objeknya sendiri agar pemanggilan dapat dirantai. Kelas berikut memanfaatkannya untuk method geser yang dapat dipanggil berturut-turut.

cpp titik-geser.cpp
#include <iostream>

class Titik {
private:
    double x = 0.0;
    double y = 0.0;

public:
    Titik& geser(double dx, double dy) {
        x += dx;
        y += dy;
        return *this;
    }

    double bacaX() const {
        return x;
    }

    double bacaY() const {
        return y;
    }
};

int main() {
    Titik p;
    p.geser(2.0, 1.0).geser(0.5, 3.5).geser(-1.0, 0.0);

    std::cout << "p.x = " << p.bacaX() << ", p.y = " << p.bacaY() << "\n";
    return 0;
}
text Keluaran
p.x = 1.5, p.y = 4.5

Tipe kembalian Titik& adalah referensi ke objek itu sendiri, sedangkan pernyataan return *this menguraikan pointer this menjadi objek yang dirujuknya. Hasil kembaliannya kemudian menjadi objek tempat panggilan geser berikutnya dilakukan, sehingga tiga pemanggilan itu terjadi pada satu objek p yang sama. Pola ini disebut method chaining dan dipakai luas pada pustaka C++ untuk kode yang membangun objek langkah demi langkah.

Member function const#

Member function const adalah member function yang menjanjikan tidak mengubah member dari objek yang memanggilnya. Janji itu ditulis dengan akhiran const setelah daftar parameter, dan bagi compiler akhiran ini berarti parameter tersembunyinya berupa pointer ke objek const. Member function semacam ini satu-satunya yang dapat dipanggil melalui objek const, sehingga penandaannya wajib untuk fungsi pembaca nilai.

cpp member-const.cpp
#include <iostream>

class Kotak {
private:
    double panjang = 10.0;
    double lebar = 4.0;

public:
    double luas() const {
        return panjang * lebar;
    }

    void skala(double faktor) {
        panjang *= faktor;
        lebar *= faktor;
    }
};

void cetakInfo(const Kotak& k) {
    std::cout << "Luas kotak = " << k.luas() << "\n";
}

int main() {
    const Kotak tetap;

    std::cout << "Luas tetap = " << tetap.luas() << "\n";
    cetakInfo(tetap);
    return 0;
}
text Keluaran
Luas tetap = 40
Luas kotak = 40

Objek tetap dinyatakan const, sehingga hanya member function const yang dapat dipanggilnya. Fungsi cetakInfo menerima objek sebagai const Kotak&, yaitu konvensi pengiriman yang menghindari penyalinan tanpa memberi kesempatan perubahan. Fungsi yang menerima parameter const Kotak& hanya dapat memanggil luas, bukan skala, dan pemeriksaannya dilakukan compiler pada waktu kompilasi.

cpp Contoh: pemanggilan yang ditolak oleh compiler
// tidak-dijalankan (member function non-const tidak boleh dipanggil melalui objek const, sehingga kompilasi gagal)
const Kotak tetap;
tetap.skala(2.0);

Keyword mutable untuk data sementara#

Kata kunci mutable menghapuskan janji const untuk satu member tertentu, sehingga member itu boleh diubah bahkan dari dalam member function const. Member yang diperlakukan demikian biasanya bukan bagian dari keadaan logis objek, melainkan detail internal seperti cache hasil perhitungan atau penghitung pemakaian. Kelas berikut menghitung jumlah kata dari teks yang tetap, dan menghitung berapa kali perhitungan itu dijalankan.

cpp mutable-cache.cpp
#include <iostream>
#include <string>

class PenghitungKata {
private:
    std::string teks;
    mutable int jumlahHitung = 0;

public:
    PenghitungKata(const std::string& t) {
        teks = t;
    }

    int hitungKata() const {
        ++jumlahHitung;
        int jumlah = 1;
        for (char c : teks) {
            if (c == ' ') {
                ++jumlah;
            }
        }
        return jumlah;
    }

    int berapaKaliDihitung() const {
        return jumlahHitung;
    }
};

int main() {
    const PenghitungKata kalimat("kucing mengejar bola di taman");

    std::cout << "Jumlah kata      = " << kalimat.hitungKata() << "\n";
    std::cout << "Jumlah kata      = " << kalimat.hitungKata() << "\n";
    std::cout << "Metode dipanggil = " << kalimat.berapaKaliDihitung() << " kali\n";
    return 0;
}
text Keluaran
Jumlah kata      = 5
Jumlah kata      = 5
Metode dipanggil = 2 kali

Objek kalimat bersifat const, tetapi setiap pemanggilan hitungKata tetap menaikkan jumlahHitung karena member itu dinyatakan mutable. Perubahan seperti ini tidak dianggap mengubah keadaan logis objek, yaitu teks yang dihitungnya. Pada penerapan nyata, member mutable lazim dipakai untuk menyimpan hasil perhitungan yang mahal, kemudian pemanggilan berikutnya mengembalikan cache tersebut tanpa menghitung ulang.

Kesalahan umum#

Kesalahan berikut paling sering muncul saat menulis member function.

  • Lupa menandai fungsi pembaca dengan const. Akibatnya objek const dan parameter const& tidak dapat memanggil fungsinya, dan errornya muncul pada pemanggil, bukan pada definisi fungsinya.
  • Mengembalikan Titik biasa pada pola chaining alih-alih Titik&. Nilai kembalian berupa salinan membuat rantai pemanggilan mengubah salinan yang langsung dibuang, sedangkan objek aslinya tidak berubah.
  • Memakai mutable untuk menghindari kesulitan const. Keputusan itu menyembunyikan perubahan dari pembaca kode, sehingga lebih baik meninjau ulang kontrak fungsinya.

Ringkasan#

  • Setiap member function menerima parameter tersembunyi berupa alamat objek pemanggilnya, dan this adalah namanya di dalam kode.
  • return *this mengembalikan objek pemanggil itu sendiri, sehingga member function dapat dirantai dalam satu pernyataan.
  • Akhiran const pada member function menjanjikan member tidak diubah, dan hanya member function itu yang dapat dipanggil melalui objek const.
  • Kata kunci mutable memberi pengecualian bagi member internal seperti cache atau penghitung agar tetap boleh berubah di dalam member function const.

Referensi#