Data di dunia nyata menumpuk: satu mahasiswa memiliki alamat, dan alamat itu sendiri terdiri atas jalan, kota, dan kode pos. Kita bisa mendefinisikan Alamat terpisah lalu memakainya sebagai member, dan itu sah. Namun C++ juga memungkinkan mendefinisikan struct di dalam badan struct lain — pola yang disebut nested struct — ketika tipe bagian itu hanya bermakna di dalam induknya. Pada Chapter ini kita menulis struct bertingkat, mengakses member dua lapis dengan operator titik, membandingkan definisi di dalam versus di luar, dan menutup dengan struct yang membawa std::vector berisi struct lain.
Struct di Dalam struct #
Definisi nested struct ditulis seperti definisi struct biasa, hanya posisinya di dalam badan induk. Tipe yang dihasilkan adalah tipe tersendiri yang namanya berkualifikasi pada induknya: Mahasiswa::Alamat. Struct induk kemudian mendeklarasikan member bertipe tersebut seperti member lain.
#include <iostream>
#include <string>
struct Mahasiswa {
std::string nama;
int nim;
// Nested struct: tipe didefinisikan di dalam tipe lain
struct Alamat {
std::string jalan;
std::string kota;
int kodePos;
};
Alamat alamat; // member bertipe nested struct
};
int main() {
Mahasiswa m;
m.nama = "Dewi Lestari";
m.nim = 10245;
m.alamat.jalan = "Jl. Mawar No. 7";
m.alamat.kota = "Bandung";
m.alamat.kodePos = 40115;
std::cout << m.nama << " (" << m.nim << ")\n";
std::cout << m.alamat.jalan << ", " << m.alamat.kota << " " << m.alamat.kodePos << "\n";
// Tipe nested dipakai berdiri sendiri lewat nama terkualifikasi
Mahasiswa::Alamat kantorProdi {"Jl. Ganesa No. 10", "Bandung", 40132};
std::cout << "Kantor prodi: " << kantorProdi.jalan << ", "
<< kantorProdi.kota << " " << kantorProdi.kodePos << "\n";
return 0;
}Output yang Dihasilkan:
Dewi Lestari (10245)
Jl. Mawar No. 7, Bandung 40115
Kantor prodi: Jl. Ganesa No. 10, Bandung 40132Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 9-13 (
struct Alamat { ... };): definisi nested struct di dalamMahasiswa. Nama lengkapnya menjadiMahasiswa::Alamat— di luar induk kita wajib menulis awalanMahasiswa::untuk merujuknya. - Baris 15 (
Alamat alamat;): memberalamatbertipe nested struct. Di dalam induk, cukup nama pendekAlamattanpa kualifikasi. - Baris 22-24 (
m.alamat.jalan = ...): akses bertingkat. Operator titik dibaca dari kiri ke kanan:madalahMahasiswa,m.alamatadalah memberAlamatmilikm, danm.alamat.kotaadalah memberkotamilik alamat tersebut. Untuk pointer bertingkat kita nanti menggabungkan->dengan., misalnyap->alamat.kota. - Baris 30 (
Mahasiswa::Alamat kantorProdi {...}): tipe nested dipakai berdiri sendiri di luar struct induk dengan nama terkualifikasi. Ini menunjukkan nested struct bukan member data — ia adalah tipe yang tinggal di dalam namespaceMahasiswa.
Definisi di Dalam vs di Luar #
Nested struct bukan satu-satunya cara — kita bisa juga mendefinisikan struct Alamat di luar, pada scope yang sama dengan Mahasiswa, lalu memakainya sebagai member. Keduanya menghasilkan kode yang bekerja identik; yang berbeda adalah cakupan namanya dan niat yang disampaikan:
- Definisi di dalam (nested): nama
Alamattidak memenuhi scope global — pemakai kode lain tidak bisa membayangkanAlamatyang tidak terkait mahasiswa. Cocok bila tipe bagian hanya dipakai oleh induknya, misalnya node internal suatu struktur data. Kekurangannya, pemakaian di luar menjadi bertele-tele:Mahasiswa::Alamat. - Definisi di luar:
Alamatbebas dipakai olehMahasiswa,Dosen, bahkanKantorsekaligus tanpa kualifikasi. Cocok bila tipe bagian digunakan banyak tipe lain. Kekurangannya, nama memenuhi scope — satu masalah kecil yang kelak ditangani rapi olehnamespacedan Chapter Namespace.
Aturan praktisnya sederhana: tipe bagian yang hanya dipakai induknya sendiri, letakkan di dalam; tipe bagian yang dipakai lintas induk, letakkan di luar. Keduanya boleh bercampur dalam satu proyek besar tanpa masalah.
Struct dengan Member Container #
Kombinasi yang paling sering kita jumpai di proyek nyata adalah struct yang salah satu membernya berupa container berisi struct lain — misalnya rak buku yang membawa banyak Buku. Struktur seperti ini menggantikan array paralel (satu vector judul, satu vector tahun) yang mudah tertukar urutannya.
#include <iostream>
#include <string>
#include <vector>
// Struct yang didefinisikan di luar (di scope yang sama) sehingga mudah dipakai ulang
struct Buku {
std::string judul;
int tahun;
};
// Struct dengan member container yang berisi struct lain
struct Rak {
std::string kode;
std::vector<Buku> isi;
};
int main() {
Rak rakA {"R-01", {}};
rakA.isi.push_back({"Pemrograman C++", 2023});
rakA.isi.push_back({"Algoritma Dasar", 2021});
std::cout << "Rak " << rakA.kode << " berisi " << rakA.isi.size() << " buku:\n";
for (const Buku& b : rakA.isi) {
std::cout << "- " << b.judul << " (" << b.tahun << ")\n";
}
return 0;
}Output yang Dihasilkan:
Rak R-01 berisi 2 buku:
- Pemrograman C++ (2023)
- Algoritma Dasar (2021)Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 18 (
Rak rakA {"R-01", {}};): aggregate initialization dari Chapter sebelumnya bekerja pada struct ber-container;{}memberiisiberupa vector kosong. - Baris 19-20 (
rakA.isi.push_back({...})): setiap elemen vector adalahBukuutuh, dan{...}di dalampush_backmengonstruksi satuBukulangsung dari daftar nilainya. - Baris 23 (
for (const Buku& b : rakA.isi)): range-based for dari Section control-flow berjalan di atas vector member. Parameterconst Buku&menghindari penyalinan tiap elemen, mengikuti kebiasaan pass-by-const-reference yang sudah kita kenal.
Pola struct-yang-membawa-container seperti Rak di atas adalah fondasi model data yang sering kita bangun di proyek nyata: kelas yang berisi daftar mahasiswa, pesanan yang berisi daftar item, atau playlist yang berisi daftar lagu. Dengan satu objek induk, seluruh koleksi datanya ikut terbawa — disalin, dikirim ke fungsi, dan dibandingkan sebagai satu kesatuan. Bila kedalaman bertingkatnya makin banyak, cukup ikuti aturan akses yang sama: operator titik dibaca dari luar ke dalam.
Referensi resmi tentang tipe yang didefinisikan di dalam tipe lain dapat dibaca pada halaman nested types
di cppreference. Setelah struktur bertingkat beres, kita berpindah ke tipe komposit lain yang mengelompokkan nilai berbeda: enum, konstanta bernama yang membuat kode bebas dari angka tanpa makna.