Mengapa objek dari kelas yang sama memiliki akses ke data pribadi satu sama lain?

98

Mengapa objek dari kelas yang sama memiliki akses ke data pribadi satu sama lain?

class TrivialClass {
public: 
  TrivialClass(const std::string& data) :
    mData(data) {};

  const std::string& getData(const TrivialClass& rhs) const {
    return rhs.mData;
  };

private:
  std::string mData;
};

int main() {
  TrivialClass a("fish");
  TrivialClass b("heads");

  std::cout << "b via a = " << a.getData(b) << std::endl;
  return 0;
}

Kode ini berfungsi. Sangat mungkin bagi objek a untuk mengakses data pribadi dari objek b dan mengembalikannya. Mengapa harus demikian? Menurut saya, data pribadi itu pribadi. (Saya mulai dengan mencoba memahami konstruktor salinan dalam idiom pimpl, tetapi kemudian saya menemukan bahwa saya bahkan tidak memahami situasi sederhana ini.)

Keith
sumber
18
Nah, sebagai titik awal, Anda tidak akan dapat menerapkan konstruktor salinan dengan benar untuk apa pun kecuali kelas yang paling sederhana. Anda dapat menganggap kelas sebagai sahabat mereka sendiri :-)
Cameron
4
Pikirkan privasi dari pelanggan mereka, tetapi semua karyawan kelas memiliki akses
Martin Beckett
Terima kasih Cameron. Itu masuk akal, tapi mengapa akses ini tidak dibatasi hanya untuk menyalin konstruktor dan operator penugasan?
Keith
5
Objek dengan tipe yang sama sering berinteraksi banyak. Dan tidak ada yang memaksa Anda untuk menulis metode yang membagikan data pribadi contoh lain. :)
UncleBens
4
Sederhananya, pada waktu kompilasi, compiler tidak memiliki cara untuk mengidentifikasi objeknya yang sama. Menerapkan akses semacam itu akan membutuhkan dukungan run-time.
Chethan

Jawaban:

80

Karena begitulah cara kerjanya di C ++. Di C ++ kontrol akses bekerja pada basis per kelas , bukan pada basis per objek.

Kontrol akses di C ++ diimplementasikan sebagai fitur waktu kompilasi statis. Saya pikir cukup jelas bahwa tidak mungkin untuk menerapkan kontrol akses per objek yang berarti pada waktu kompilasi. Hanya kontrol per kelas yang dapat diterapkan dengan cara itu.

Beberapa petunjuk tentang kontrol per objek terdapat dalam spesifikasi akses yang dilindungi , itulah sebabnya ia bahkan memiliki bab tersendiri dalam standar (11.5). Tapi tetap saja, fitur per objek yang dijelaskan di sana masih belum sempurna. Sekali lagi, kontrol akses di C ++ dimaksudkan untuk bekerja pada basis per kelas.

Semut
sumber
9
+1. C ++ sangat ahli dalam mekanisme waktu kompilasi, tidak terlalu besar pada mekanisme run-time. Aturan umum yang cukup bagus.
Nemo
4
"Anda tidak benar-benar mungkin menerapkan kontrol akses per objek yang berarti pada waktu kompilasi". Kenapa tidak? Dalam void X::f(X&x), penyusun dengan mudah mampu membedakan this->adan x.a. Tidak (selalu) mungkin bagi kompilator untuk mengetahui hal itu *thisdan xsebenarnya adalah objek yang sama jika x.f(x)dipanggil, tetapi saya bisa melihat desainer bahasa menemukan ini OK.
André Caron
@ AndréCaron Saya pikir ini sebenarnya adalah ketel ikan yang jauh lebih besar daripada yang Anda bayangkan. Ketika inlining tidak terjadi, kompilator harus selalu melakukan pemeriksaan apakah thisdan &xadalah sama. Untuk membuatnya lebih buruk ini sebenarnya berakhir menjadi masalah bahkan dengan X::f(Y& y), karena objek konkret kita bisa menjadi tipe Zyang mewarisi dari Xdan Y. Singkatnya, ini benar-benar berantakan, tidak berkinerja baik, sulit untuk bekerja dengan MI.
Nir Friedman
@NirFriedman Saya pikir Anda salah paham dengan saran itu. Saat mengompilasi X::f(X& x), jika ada akses ke x.a, itu tidak akan dikompilasi. Tidak ada perubahan lain, tidak ada pemeriksaan yang perlu dimasukkan, sehingga kinerja program yang masih valid tidak terpengaruh. Dan itu tidak disarankan sebagai perubahan besar pada C ++ yang sudah ada, tetapi sebagai sesuatu yang desainer bisa lakukan saat memperkenalkan privateaslinya.
Alexey Romanov
31

"Pribadi" sebenarnya bukan mekanisme kontrol akses dalam arti "Saya membuat foto saya di facebook menjadi pribadi sehingga Anda tidak dapat melihatnya."

Di C ++, "private" hanya mengatakan ini adalah bagian dari kelas yang Anda (pembuat kode kelas) mungkin ubah di versi mendatang, dll., Dan Anda tidak ingin pembuat kode lain menggunakan kelas Anda untuk mengandalkan keberadaan atau fungsionalitas mereka .

Jika Anda menginginkan kontrol akses yang benar, Anda harus menerapkan teknik keamanan data asli.

vsekhar
sumber
13

Ini adalah pertanyaan yang bagus dan saya telah menemukan pertanyaan ini baru-baru ini. Saya berdiskusi dengan rekan-rekan saya dan inilah ringkasan dari diskusi kami: Ini memang disengaja. Ini tidak berarti desain ini benar-benar masuk akal untuk semua kasus, tetapi harus ada beberapa pertimbangan mengapa privat per kelas dipilih. Kemungkinan alasan yang dapat kami pikirkan meliputi:

Pertama-tama, biaya kontrol akses per instance bisa sangat tinggi. Ini telah dibahas oleh orang lain di utas ini. Secara teori, ini dapat dilakukan melalui pemeriksaan penunjuk ini . Namun, ini tidak dapat dilakukan pada waktu kompilasi, dan hanya dapat dilakukan pada waktu proses. Jadi, Anda harus mengidentifikasi kontrol akses setiap anggota pada waktu proses, dan jika dilanggar mungkin hanya pengecualian yang akan dimunculkan. Biayanya tinggi.

Kedua, kontrol akses per kelas memiliki kasus penggunaannya sendiri, seperti copy constructor atau operator =. Akan sulit untuk menerapkannya jika kontrol akses per instance.

Selain itu, kontrol akses terutama dari perspektif pemrograman / bahasa, untuk bagaimana memodulisasi / mengontrol akses ke kode / anggota, bukan datanya.

JackyZhu
sumber
12

Ini semacam keputusan desain bahasa yang sewenang-wenang. Di Ruby , misalnya, privatebenar-benar berarti pribadi, seperti dalam "hanya instance yang dapat mengakses anggota data pribadinya sendiri". Namun, ini agak membatasi.

Seperti yang ditunjukkan dalam komentar, pembuat salinan dan operator penugasan adalah tempat umum di mana Anda mengakses anggota data pribadi instance lain secara langsung. Ada alasan yang kurang jelas mengapa.

Perhatikan kasus berikut ini. Anda menerapkan daftar tertaut OO. Daftar tertaut memiliki kelas node bersarang untuk mengelola pointer. Anda dapat mengimplementasikan kelas node ini sedemikian rupa sehingga ia mengelola penunjuk itu sendiri (daripada menjadikan penunjuknya publik dan dikelola oleh daftar). Dalam kasus seperti itu, Anda akan memiliki objek node yang ingin memodifikasi pointer objek node lain di tempat lain yang merupakan copy konstruktor dan operator penugasan.

André Caron
sumber
4

Triknya adalah dengan mengingat bahwa datanya adalah privateke kelas , bukan ke instance kelas. Setiap metode dalam kelas Anda dapat mengakses data pribadi dari setiap instance kelas itu; tidak ada cara untuk menjaga kerahasiaan data dalam sebuah instance kecuali Anda melarang metode yang secara eksplisit mengakses anggota data pribadi dari instance lain.

Adam Maras
sumber
1

Selain semua jawaban di atas, pertimbangkan konstruktor salinan kustom, operator tugas, dan semua fungsi lain yang akan Anda tulis untuk kelas yang beroperasi pada instance lain . Anda akan membutuhkan fungsi pengakses untuk semua anggota data tersebut.

Yakub
sumber
-8

Data pribadi tetap bersifat pribadi sampai seseorang yang memiliki akses ke data tersebut mengungkapkannya kepada orang lain.

Konsep ini juga berlaku untuk situasi lain, seperti:

class cMyClass
{
public:
   // ...
   // omitted for clarity
   // ...

   void Withdraw(int iAmount)
   {
      iTheSecretVault -= iAmount;
   }

private:
   int iTheSecretVault;
};

Bagaimana orang bisa menarik uang itu? :)

YeenFei
sumber
3
Contoh ini tidak melibatkan satu instance kelas yang mengakses anggota data pribadi instance lain.
André Caron
@Andre, "Konsep ini juga berlaku untuk situasi lain, seperti ..."
YeenFei
^ "situasi lain" menurut definisinya di luar topik, jadi contoh Anda tidak ada relevansinya (dan saya juga tidak yakin ini akan informatif di tempat lain)
underscore_d
1
Ini sama sekali bukan penjelasan yang benar untuk pertanyaan itu.
Panda