
Definisi
Enkapsulasi adalah seperangkat alat untuk mengontrol akses ke data atau metode yang mengelola data itu. Definisi terperinci dari istilah "enkapsulasi" dapat ditemukan dalam publikasi saya sebelumnya tentang Habrรฉ di tautan ini. Artikel ini berfokus pada contoh enkapsulasi dalam C ++ dan C.
Enkapsulasi dalam C ++
Secara default, dalam suatu kelas ( class
) data dan metode bersifat pribadi ( private
); mereka hanya dapat dibaca dan dimodifikasi oleh kelas tempat mereka berada. Tingkat akses dapat diubah menggunakan kata kunci yang sesuai yang disediakan C ++.
Beberapa kualifikasi tersedia dalam C ++, dan mereka memodifikasi akses data sebagai berikut:
- data publik tersedia untuk semua orang;
- protected (
protected
) - hanya tersedia untuk kelas dan kelas anak; - privat -
private
- hanya tersedia untuk kelas di mana mereka berada.
Untuk singkatnya, hanya dua tingkat (swasta dan publik) yang akan disorot dalam contoh.
Contoh enkapsulasi
Di kelas Contact
, variabel dan metode publik dapat diakses dari program utama. Variabel dan metode pribadi hanya dapat dibaca, dipanggil, atau diubah oleh kelas itu sendiri.
#include <iostream> using namespace std; class Contact { private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor { mobile_number = 12345678; home_number = 87654321; } void print_numbers() { cout << "Mobile number: " << mobile_number; cout << ", home number: " << home_number << endl; } }; int main() { Contact Tony; Tony.print_numbers(); // cout << Tony.mobile_number << endl; // will cause compile time error return 0; }
Mencoba mencetak atau memodifikasi variabel private mobile_number
dari program main
( main
) akan menyebabkan kesalahan kompilasi karena akses ke data pribadi di kelas terbatas.
Pelanggaran Enkapsulasi dengan Teman (Praktik yang Baik)
Di C ++, ada kata kunci โtemanโ yang memungkinkan Anda menambahkan pengecualian pada aturan umum untuk mengakses data. Jika suatu fungsi atau kelas disebut sebagai teman dari kelas Contact
, mereka mendapatkan akses gratis ke data pribadi atau dilindungi.
Ada dua aturan dasar persahabatan - persahabatan tidak diwariskan dan tidak saling menguntungkan. Juga, kehadiran "teman" tidak mengubah tingkat keamanan data - data pribadi tetap pribadi dengan pengecualian "teman".
#include <iostream> using namespace std; class Contact { private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor { mobile_number = 12345678; home_number = 87654321; } // Declaring a global 'friend' function friend void print_numbers( Contact some_contact ); }; void print_numbers( Contact some_contact ) { cout << "Mobile number: " << some_contact.mobile_number; cout << ", home number: " << some_contact.home_number << endl; } int main() { Contact Tony; print_numbers(Tony); return 0; }
Dalam contoh ini, fungsi print_numbers()
adalah fungsi normal, bukan metode kelas Contact
. Mendeklarasikan fungsi print_numbers()
"teman" kelas Contact
adalah satu-satunya alasan fungsi print_numbers()
memiliki akses ke data pribadi. Jika Anda menghapus baris dengan definisi teman, kode tidak akan dikompilasi.
Catatan : lebih baik tidak menyalahgunakan teman. Menambahkan teman harus dianggap sebagai pengecualian, bukan sebagai praktik umum.
Pelanggaran Enkapsulasi dengan Konversi dan Pointer Jenis (Praktik Buruk)
Pertama-tama, perlu dicatat bahwa menggunakan pointer dan ketik konversi dengan cara ini adalah ide yang buruk. Metode ini tidak menjamin penerimaan data yang diperlukan. Itu kurang dibaca dan tidak dipelihara. Meskipun demikian, ia ada.
C ++ mewarisi banyak alat dari C, salah satunya adalah typecasting
. Secara default, semua variabel dan metode di kelas bersifat pribadi. Pada saat yang sama, tingkat standar akses data dalam struktur ( struct
) adalah publik. Dimungkinkan untuk membuat struktur atau kelas yang sepenuhnya publik di mana data akan ditempatkan secara identik dengan data dalam kelas Contact
dan menggunakan konversi tipe untuk mengakses data pribadi.
#include <iostream> using namespace std; class Contact { private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor { mobile_number = 12345678; home_number = 87654321; } void print_numbers() { cout << "Mobile number: " << mobile_number; cout << ", home number: " << home_number << endl; } }; struct Contact_struct { int mobile_number; int home_number; }; int main() { Contact Tony; Contact_struct * structured_Tony; Tony.print_numbers(); structured_Tony = (Contact_struct *) & Tony; structured_Tony->mobile_number = 20; structured_Tony->home_number = 30; Tony.print_numbers(); return 0; }
Data pribadi telah dibaca dan dimodifikasi karena konversi jenis
Enkapsulasi C
Enkapsulasi secara tradisional dianggap sebagai salah satu prinsip utama OOP. Namun, ini tidak membatasi penggunaan prinsip ini dalam bahasa yang berorientasi prosedural. Di C, enkapsulasi telah digunakan untuk waktu yang lama, meskipun tidak ada kata kunci "pribadi" dan "publik".
Variabel pribadi
Dalam konteks enkapsulasi, semua data dalam C dapat dianggap publik secara default. Tingkat akses ke variabel dalam struktur ( struct
) dapat diubah menjadi pribadi jika definisi mereka diisolasi dari program utama. Efek yang diinginkan dapat dicapai dengan menggunakan file header (header, .h) dan sumber (source, .c) yang terpisah.
Dalam contoh ini, struktur didefinisikan dalam file sumber terpisah "private_var.c". Karena menginisialisasi struktur dalam C memerlukan pengalokasian dan membebaskan memori, beberapa fungsi pembantu telah ditambahkan.
#include "private_var.h" #include <stdio.h> #include <stdlib.h> struct Contact { int mobile_number; int home_number; }; struct Contact * create_contact() { struct Contact * some_contact; some_contact = malloc(sizeof(struct Contact)); some_contact->mobile_number = 12345678; some_contact->home_number = 87654321; return( some_contact ); } void delete_contact( struct Contact * some_contact ) { free(some_contact); }
Dalam file header yang sesuai "private_var.h", struktur Contact
dideklarasikan, tetapi isinya tetap tersembunyi ke program utama.
#ifndef PRIVATE_VAR #define PRIVATE_VAR struct Contact; struct Contact * create_contact(); void delete_contact( struct Contact * some_contact ); #endif
Dengan demikian, untuk "main.c" isi struktur tidak diketahui dan upaya untuk membaca atau memodifikasi data pribadi akan menyebabkan kesalahan kompilasi.
#include "private_var.h" #include <stdio.h> int main() { struct Contact * Tony; Tony = create_contact(); // printf( "Mobile number: %d\n", Tony->mobile_number); // will cause compile time error delete_contact( Tony ); return 0; }
Mengakses variabel pribadi dengan Pointer
Konversi tipe dapat digunakan untuk mengatasi enkapsulasi dalam C serta C ++, tetapi pendekatan ini telah dijelaskan. Mengetahui bahwa dalam struktur data diatur dalam urutan deklarasi mereka, pointer dan aritmatika pointer cocok untuk mencapai tujuan.
Akses ke variabel dalam struktur terbatas. Namun, hanya variabel yang disembunyikan, bukan memori tempat data disimpan. Pointer dapat dianggap sebagai referensi ke alamat memori, dan jika memori ini tersedia untuk program, data yang disimpan dalam memori ini dapat dibaca dan diubah. Jika pointer ditugaskan ke memori di mana struktur menyimpan datanya - mereka dapat dibaca. Menggunakan definisi struktur yang sama (file ".c" dan ".h" yang sama) dan file "main.c" yang dimodifikasi, pembatasan akses diatasi.
#include "private_var.h" #include <stdio.h> int main() { struct Contact * Tony; Tony = create_contact(); int * mobile_number_is_here = (int *)Tony; printf("Mobile number: %d\n", *mobile_number_is_here); int * home_number_is_here = mobile_number_is_here + 1; *home_number_is_here = 1; printf("Modified home number: %d\n", *home_number_is_here); delete_contact( Tony ); return 0; }
Data dalam struktur telah dibaca dan dimodifikasi
Fungsi pribadi
Fungsi, sebagai eksternal ( extern
) secara default, terlihat di seluruh translation unit
disebut. Dengan kata lain, jika beberapa file dikompilasi bersama menjadi satu file objek, salah satu file ini akan dapat mengakses fungsi apa pun dari file lainnya. Menggunakan static
"statis" saat membuat fungsi akan membatasi visibilitasnya ke file yang didefinisikan. Oleh karena itu, untuk memastikan privasi fungsi, Anda perlu melakukan beberapa langkah:
- fungsi harus dinyatakan statis (
static
) baik dalam file sumber (.c) atau dalam file header yang sesuai (.h); - definisi fungsi harus dalam file sumber terpisah.
Dalam contoh ini, fungsi statis print_numbers()
didefinisikan dalam file "private_funct.c". By the way, fungsi delete_contact()
berhasil memanggil print_numbers()
karena mereka berada di file yang sama.
#include "private_funct.h" #include <stdio.h> #include <stdlib.h> struct Contact { int mobile_number; int home_number; }; struct Contact * create_contact() { struct Contact * some_contact; some_contact = malloc(sizeof(struct Contact)); some_contact->mobile_number = 12345678; some_contact->home_number = 87654321; return( some_contact ); } static void print_numbers( struct Contact * some_contact ) { printf("Mobile number: %d, ", some_contact->mobile_number); printf("home number = %d\n", some_contact->home_number); } void delete_contact( struct Contact * some_contact ) { print_numbers(some_contact); free(some_contact); }
Dalam file header yang sesuai "private_funct.h", print_numbers()
dinyatakan sebagai fungsi statis.
#ifndef PRIVATE_FUNCT_H #define PRIVATE_FUNCT_H struct Contact; struct Contact * create_contact(); static void print_numbers( struct Contact * some_contact ); void delete_contact( struct Contact * my_points ); #endif
Program utama, "main.c", berhasil memanggil print_numbers()
secara tidak langsung melalui delete_contact()
, karena kedua fungsi berada dalam dokumen yang sama. Namun, upaya untuk memanggil print_numbers()
dari program utama akan print_numbers()
kesalahan.
#include "private_funct.h" #include <stdio.h> int main() { struct Contact * Tony; Tony = create_contact(); // print_numbers( Tony ); // will cause compile time error delete_contact( Tony ); return 0; }
Mengakses Fitur Pribadi
print_numbers()
memanggil print_numbers()
dari program utama. Untuk melakukan ini, Anda dapat menggunakan kata kunci goto
atau meneruskan pointer ke fungsi pribadi di main
. Kedua metode memerlukan perubahan baik dalam file sumber "private_funct.c", atau langsung di tubuh fungsi itu sendiri. Karena metode ini tidak mem-bypass enkapsulasi dan membatalkannya, mereka berada di luar cakupan artikel ini.
Kesimpulan
Enkapsulasi ada di luar bahasa OOP. Bahasa OOP modern membuat penggunaan enkapsulasi menjadi nyaman dan alami. Ada banyak cara untuk menghindari enkapsulasi dan menghindari praktik yang dipertanyakan akan membantu melestarikannya dalam C dan C ++.