Mengapa variabel pribadi dijelaskan dalam file header yang dapat diakses publik?

11

OK, jadi mudah-mudahan ini adalah pertanyaan yang cukup subjektif untuk Programmer, tetapi begini saja. Saya terus memperluas pengetahuan saya tentang bahasa dan praktik rekayasa perangkat lunak ... dan saya telah mengalami sesuatu yang tidak masuk akal bagi saya sama sekali.

Dalam C ++, deklarasi kelas menyertakan private:metode dan parameter dalam file header, yang, secara teoritis, adalah apa yang Anda berikan kepada pengguna untuk disertakan jika Anda menjadikannya lib.

Di Objective-C, @interfacelakukan hal yang hampir sama, memaksa Anda untuk membuat daftar anggota pribadi Anda (setidaknya, ada cara untuk mendapatkan metode pribadi dalam file implementasi).

Dari apa yang dapat saya katakan, Java dan C # memungkinkan Anda untuk menyediakan antarmuka / protokol yang dapat mendeklarasikan semua properti / metode yang dapat diakses publik dan memberi koder kemampuan untuk menyembunyikan semua detail implementasi dalam file implementasi.

Mengapa? Enkapsulasi adalah salah satu prinsip utama OOP, mengapa C ++ dan Obj-C tidak memiliki kemampuan dasar ini? Apakah ada semacam praktik kerja terbaik untuk Obj-C atau C ++ yang menyembunyikan semua implementasi?

Terima kasih,

Stephen Furlani
sumber
4
Aku merasakan sakitmu. Saya lari berteriak dari C ++ pertama kali saya menambahkan bidang pribadi ke kelas dan harus mengkompilasi ulang semua yang menggunakannya.
Larry Coleman
@Larry, saya tidak terlalu mempermasalahkannya, tetapi sepertinya ini digembar-gemborkan sebagai bahasa OO yang hebat, tetapi bahkan tidak bisa merangkum "dengan benar."
Stephen Furlani
2
Jangan percaya hype. Ada cara yang lebih baik untuk melakukan OO, baik di kamp pengetikan statis dan dinamis.
Larry Coleman
Dalam beberapa hal, ini adalah bahasa yang hebat, tetapi bukan karena kemampuan OO-nya, yang merupakan pencangkokan Simula 67 ke C.
David Thornley
Anda harus melakukan pemrograman C. Maka Anda akan memiliki pemahaman yang lebih baik tentang mengapa bahasa-bahasa ini seperti itu, termasuk Java C # dll.
Henry

Jawaban:

6

Pertanyaannya adalah apakah kompiler perlu tahu seberapa besar suatu objek. Jika demikian, maka kompiler harus tahu tentang anggota pribadi untuk menghitungnya.

Di Jawa, ada tipe dan objek primitif, dan semua objek dialokasikan secara terpisah, dan variabel yang mengandungnya benar-benar pointer. Oleh karena itu, karena pointer adalah objek ukuran tetap, kompiler tahu seberapa besar hal yang diwakili variabel, tanpa mengetahui ukuran sebenarnya dari objek yang diarahkan ke. Konstruktor menangani semua itu.

Di C ++, dimungkinkan untuk memiliki objek yang diwakili secara lokal atau di heap. Oleh karena itu, kompiler perlu mengetahui seberapa besar suatu objek, sehingga dapat mengalokasikan variabel atau array lokal.

Kadang-kadang diinginkan untuk membagi fungsionalitas kelas menjadi antarmuka publik dan semua hal pribadi lainnya, dan di situlah teknik PIMPL (Pointer ke IMPLementation) masuk. Kelas akan memiliki pointer ke kelas implementasi pribadi, dan metode kelas publik dapat memanggil bahwa.

David Thornley
sumber
tidak dapat kompiler melihat ke perpustakaan objek untuk mendapatkan informasi ini? Mengapa informasi ini tidak dapat dibaca mesin dan bukan dibaca manusia?
Stephen Furlani
@Stephen: Pada saat kompilasi, tidak perlu perpustakaan objek. Karena ukuran objek mempengaruhi kode yang dikompilasi, itu harus diketahui pada waktu kompilasi, bukan hanya pada waktu tautan. Lebih lanjut, mungkin bagi Ah untuk mendefinisikan kelas A, Bh untuk mendefinisikan kelas B, dan definisi fungsi dalam A.cpp untuk menggunakan objek dari kelas B dan sebaliknya. Dalam hal ini, perlu untuk mengkompilasi masing-masing A.cpp dan B.cpp sebelum yang lain, jika mengambil ukuran objek dari kode objek.
David Thornley
tidak dapat menautkan dilakukan saat kompilasi? Saya mengaku tidak mengetahui detail pada kedalaman itu, tetapi jika antarmuka objek mengekspos isinya, tidak bisakah konten yang sama terpapar dari perpustakaan atau komponen yang dapat dibaca mesin lainnya? haruskah mereka didefinisikan dalam file header yang dapat diakses publik? Saya mengerti ini adalah bagian dari sebagian besar bahasa C, tapi apakah itu memiliki begitu?
Stephen Furlani
1
@Stephen: Menghubungkan hanya dapat dilakukan selama kompilasi jika semua komponen yang sesuai ada di sana. Bahkan kemudian, itu akan menjadi proses yang rumit, melibatkan kompilasi parsial (semuanya kecuali ukuran objek), parsial menghubungkan (untuk mendapatkan ukuran objek), kemudian kompilasi akhir dan menghubungkan. Mengingat bahwa C dan C ++ adalah bahasa yang relatif lama, itu tidak akan terjadi. Mungkin seseorang bisa mendesain bahasa baru tanpa file header, tetapi itu bukan C ++.
David Thornley
14

Karena desain C ++, untuk membuat objek pada stack, kompiler harus tahu seberapa besar itu. Untuk melakukan ini, semua bidang harus ada dalam file header, karena hanya itu yang dapat dilihat oleh kompiler ketika header disertakan.

Misalnya jika Anda mendefinisikan kelas

class Foo {
    public int a;
    private int b;
};

lalu sizeof(Foo)adalah sizeof(a) + sizeof(b). Jika ada beberapa mekanisme untuk memisahkan bidang pribadi, maka header mungkin berisi

class Foo {
    public int a;
};

dengan sizeof(Foo) = sizeof(a) + ???.

Jika Anda ingin benar-benar menyembunyikan data pribadi, coba idiom pimpl, dengan

class FooImpl;
class Foo {
    private FooImpl* impl;
}

di header dan definisi untuk FooImplhanya di Foo's file implementasi.

Scott Wales
sumber
Oh Saya tidak tahu ini masalahnya. Apakah itu sebabnya bahasa seperti Java, C #, dan Obj-C memiliki "Class Objects" sehingga mereka dapat bertanya pada kelas seberapa besar objek seharusnya?
Stephen Furlani
4
Coba gunakan pimpl, pointer ke implementasi pribadi. Karena semua pointer kelas memiliki ukuran yang sama Anda tidak perlu mendefinisikan kelas implementasi, hanya mendeklarasikannya.
Scott Wales
Saya pikir itu harus menjadi jawaban, bukan komentar.
Larry Coleman
1

Ini semua bermuara pada pilihan desain.

Jika Anda benar-benar ingin menyembunyikan detail implementasi privat dari kelas C ++ atau Objective-C, maka Anda menyediakan satu atau lebih antarmuka yang didukung oleh kelas (kelas virtual murni C ++, Objective-C @protocol) dan / atau Anda membuat kelas mampu membangun sendiri dengan menyediakan metode pabrik statis atau objek pabrik kelas.

Alasan bahwa variabel pribadi diekspos dalam file header / deklarasi kelas / antarmuka @ adalah bahwa konsumen kelas Anda mungkin perlu membuat instance baru dan dan new MyClass()atau [[MyClass alloc]init]dalam kode klien perlu kompiler untuk memahami seberapa besar MyClass objek untuk melakukan alokasi.

Java dan C # juga memiliki variabel pribadi mereka yang terperinci di kelas - mereka tidak terkecuali untuk ini, tetapi IMO paradigma antarmuka jauh lebih umum dengan bahasa-bahasa tersebut. Anda mungkin tidak memiliki kode sumber di setiap kasus, tetapi ada cukup metadata dalam kode / byte yang dikompilasi untuk menyimpulkan informasi ini. Karena C ++ dan Objective-C tidak memiliki metadata ini, satu-satunya pilihan adalah detail sebenarnya dari antarmuka kelas / @. Di dunia C ++ COM, Anda tidak mengekspos variabel pribadi dari kelas apa pun yang dapat menyediakan file header - karena kelasnya adalah virtual murni. Objek pabrik kelas terdaftar untuk membuat instance aktual juga, dan ada beberapa metadata dalam berbagai bentuk.

Dalam C ++ dan Objective-C, lebih sedikit pekerjaan untuk membagikan file header dibandingkan dengan menulis dan memelihara file antarmuka / protokol @ tambahan. Ini adalah salah satu alasan mengapa Anda sering melihat implementasi pribadi begitu terbuka.

Alasan lain dalam C ++ adalah templat - kompiler perlu mengetahui detail kelas untuk menghasilkan versi kelas yang khusus untuk parameter yang disediakan. Ukuran anggota kelas akan bervariasi dengan parameterisasi, sehingga perlu memiliki informasi ini.

JBRWilkinson
sumber