Class pada dasarnya adalah daftar member: data member yang menyimpan state dan member function yang mengoperasikannya. Setelah mengenal access specifiers, kita kini bisa menyusun keduanya dengan rapi — data di bagian private, antarmuka di bagian public. Chapter ini memoles detail yang selama ini kita pakai tanpa dijelaskan tuntas: cara memberi nilai awal langsung pada deklarasi data member, makna awalan NamaClass:: pada definisi fungsi di luar class, dan dua operator untuk memanggil member dari object.

Mendeklarasikan Data Member #

Data member dideklarasikan di dalam badan class dengan pola yang sama seperti deklarasi variable biasa: tipe, lalu nama. Bedanya, deklarasi ini tidak membuat variable baru langsung — ia mendeskripsikan bahwa setiap object yang dibuat dari class nantinya akan menyimpan data tersebut. Karena itu kita boleh mendeklarasikan member dengan tipe apa pun yang sudah dikenal compiler, termasuk int, double, std::string, std::vector, bahkan class lain yang sudah kita tulis.

Satu batasan penting: kita tidak bisa mendeklarasikan data member bertipe class yang sedang kita definisikan sendiri, karena ukurannya tidak akan pernah selesai dihitung — class Node { Node dalam; }; ditolak compiler. Yang sah justru member berupa pointer atau reference ke class itu sendiri, dan pola ini nantinya menjadi dasar struktur data seperti linked list. Untuk sekarang, cukup diingat bahwa member “berisi dirinya sendiri” tidak mungkin, sedangkan “menunjuk dirinya sendiri” sah.

In-class Member Initializer #

Sejak C++11, kita boleh memberi nilai awal langsung pada deklarasi data member:

cpp
class Titik {
    int x = 0;        // in-class member initializer
    int y = 0;
};

Nilai ini dipakai otomatis setiap kali object dibuat, kecuali constructor menggantinya dengan nilai lain. Fitur kecil ini berdampak besar: tanpa itu, member bertipe fundamental yang baru dibuat berisi nilai tak tentu, dan setiap constructor harus mengulang penugasan awal yang sama. Dengan in-class initializer, nilai default tinggal di satu tempat dan tidak ada jalur pembuatan object yang melewatkannya.

Bentuk penulisannya memakai tanda sama dengan (int x = 0;) atau kurung kurawal (int x{0};). Untuk member sederhana keduanya setara, dan tanda sama dengan lebih lazim ditemui di kode nyata. Perlu dicatat bahwa in-class initializer bukan constructor — ia hanya nilai awal member — tetapi keduanya bekerja sama: constructor kita pelajari mulai Chapter berikutnya, dan ia boleh menimpa nilai-nilai default ini.

Member Function: Deklarasi, Definisi, dan Scope :: #

Member function bisa kita tulis penuh di dalam class, atau dideklarasikan di dalam lalu didefinisikan di luar class. Untuk pilihan kedua, definisi di luar wajib memakai scope operator :: setelah nama class: void Titik::cetak() { ... }. Tanpa awalan Titik::, compiler menganggap kita menulis fungsi bebas (free function) yang tidak punya akses ke data member — dan itu error.

Awalan ini bukan formalitas kosong. Ia memberi tahu compiler dua hal sekaligus: fungsi tersebut adalah member dari class Titik, dan di dalam badannya, nama-nama seperti x dan y merujuk ke data member object pemanggil. Fungsi member yang didefinisikan di dalam class otomatis inline; yang didefinisikan di luar tidak, kecuali kita menandainya eksplisit dengan kata kunci inline. Untuk materi kita, perbedaan performanya bisa diabaikan — pilihlah berdasarkan keterbacaan.

Memanggil Member: Operator . dan -> #

Kita sudah terbiasa memanggil member lewat operator titik: p.cetak(). Karena materi pointer sudah kita kuasai, ada satu pola lagi yang wajib nyaman dipakai: bila kita memegang pointer ke object, akses member-nya memakai operator panah ->. ptr->cetak() adalah kependekan dari (*ptr).cetak() — dereferensikan pointer, lalu akses member-nya. Dua baris berikut identik maknanya:

cpp
Titik* ptr = &p;
ptr->cetak();        // bentuk yang lazim
(*ptr).cetak();      // makna yang sama, lebih bertele-tele

Dalam praktik, -> hampir selalu dipilih karena lebih pendek dan tidak menumpuk tanda kurung. Operator mana yang dipakai ditentukan oleh yang kita pegang: object memakai ., pointer ke object memakai ->. Keduanya akan terus menyertai kita sampai akhir course.

Contoh Lengkap: Semua dalam Satu Class #

Program berikut merangkum materi chapter ini: in-class initializer, fungsi di dalam dan di luar class, serta pemanggilan lewat . dan ->.

cpp
#include <iostream>

class Titik {
public:
    // Data member dengan in-class member initializer: x dan y otomatis 0
    int x = 0;
    int y = 0;

    // Member function yang didefinisikan di dalam class
    void geser(int dx, int dy) {
        x += dx;
        y += dy;
    }

    // Hanya deklarasi; definisi lengkapnya ada di luar class
    void cetak();
};

// Definisi di luar class: awalan Titik:: memberi tahu compiler
// bahwa fungsi ini adalah member dari class Titik
void Titik::cetak() {
    std::cout << "(" << x << ", " << y << ")\n";
}

int main() {
    Titik p;
    p.cetak();          // memanggil method lewat object, memakai operator .

    p.geser(2, 3);
    p.cetak();

    Titik* ptr = &p;    // pointer ke object (materi pointer sudah kita kuasai)
    ptr->geser(-1, 5);  // akses member lewat pointer memakai operator ->
    ptr->cetak();

    std::cout << "x = " << p.x << ", y = " << p.y << "\n";

    return 0;
}

Output yang Dihasilkan:

text
(0, 0)
(2, 3)
(1, 8)
x = 1, y = 8

Bedah baris demi baris:

  1. Baris 6-7 (int x = 0; int y = 0;): in-class member initializer. Baris pertama output — (0, 0) — membuktikan object baru otomatis memperoleh nilai 0 tanpa ada penugasan di main().
  2. Baris 10-14 (void geser(...)): member function di dalam class. Ia mengubah x dan y milik object pemanggil; p.geser(2, 3) menghasilkan (2, 3).
  3. Baris 21-24 (void Titik::cetak() { ... }): definisi di luar class dengan scope operator. Fungsinya membaca x dan y tanpa parameter apa pun karena ia otomatis bekerja pada object pemanggil.
  4. Baris 33-36 (Titik* ptr = &p; ptr->geser(-1, 5);): kita menyimpan alamat p dalam pointer, lalu memanggil geser() lewat ->. Object yang berubah tetap p sendiri — ptr hanyalah jalan lain menuju object yang sama.
  5. Baris 38 (std::cout << "x = " ...): membuktikan perubahan lewat pointer terlihat dari object aslinya: (1, 8) adalah hasil (2, 3) digeser (-1, 5).

Rangka class kita kini lengkap: definisi, access control, data member, dan member function. Satu bagian masih dilakukan manual — pengisian nilai awal di main() lewat fungsi seperti buka(). Chapter berikutnya memperkenalkan constructor, mekanisme resmi C++ untuk mengisi object sejak detik pertama ia dibuat.

Rujukan resmi: Data members (cppreference) , Member functions (cppreference) .