Mengapa saya dapat mengakses variabel privat di konstruktor salinan?

88

Saya telah belajar bahwa saya tidak pernah dapat mengakses variabel privat, hanya dengan fungsi get di kelas. Tetapi mengapa saya dapat mengaksesnya di konstruktor salinan?

Contoh:

Field::Field(const Field& f)
{
  pFirst = new T[f.capacity()];

  pLast = pFirst + (f.pLast - f.pFirst);
  pEnd  = pFirst + (f.pEnd - f.pFirst);
  std::copy(f.pFirst, f.pLast, pFirst);
}

Deklarasi saya:

private:
  T *pFirst,*pLast,*pEnd;
demonking
sumber
Karena konstruktor salinan adalah anggota kelas secara default, dan begitu juga beberapa lainnya.
DumbCoder
+ 53 / -0? Siapa yang memilih ini? Bagaimana lagi Anda akan menyalinnya ?!? (Mari kita singkirkan non-alternatif: Buat pengambil referensi publik untuk setiap anggota pribadi? Maka mereka sama sekali tidak pribadi. Buat const&pengambil publik atau berdasarkan nilai untuk masing-masing? Maka mereka hanya 'tulis-pribadi', & untuk nilai-nilai menyia-nyiakan sumber daya & gagal untuk anggota yang tidak dapat disalin.) Saya bingung dengan keberhasilan pertanyaan hampa seperti itu, menanyakan tentang konstruksi-salinan sementara sama sekali mengabaikan artinya, & tidak ada jawaban yang menggunakan logika dasar untuk menyanggahnya. Mereka menjelaskan teknis kering, tetapi ada jawaban yang jauh lebih sederhana untuk pertanyaan yang berkedip ini
underscore_d
9
@underscore_d, "Bagaimana lagi Anda akan menyalinnya?" adalah respon yang sangat aneh menurut saya. Ini seperti menjawab "bagaimana gravitasi bekerja?" dengan "bagaimana lagi hal-hal akan jatuh!" Enkapsulasi level kelas yang membingungkan dengan enkapsulasi level objek sebenarnya cukup umum. Sungguh lucu bagaimana Anda tampaknya berpikir bahwa itu pertanyaan bodoh dan jawabannya harus jelas. Perlu diingat bahwa enkapsulasi dalam Smalltalk (bisa dibilang bahasa OO pola dasar) sebenarnya berfungsi pada tingkat objek.
aioobe
@aioobe Poin yang bagus, terima kasih. Komentar saya agak ekstrim - mungkin mesin kopi rusak hari itu. Saya menghargai Anda menunjukkan mengapa pertanyaan ini akan populer, terutama di antara mereka yang berasal dari bahasa OO lain (dan mungkin lebih). Faktanya, dapat diperdebatkan bahwa komentar saya adalah hal yang "berkedip", karena saya menulis dari sudut pandang seseorang yang sebagian besar memprogram dalam C ++. Juga, suka analogi gravitasi itu!
underscore_d

Jawaban:

33

IMHO, jawaban yang ada melakukan pekerjaan yang buruk menjelaskan "Mengapa" ini - terlalu berfokus pada mengulangi perilaku apa yang valid. "pengubah akses bekerja pada tingkat kelas, dan bukan pada tingkat objek." - iya tapi kenapa?

Konsep menyeluruh di sini adalah bahwa programmer merancang, menulis dan memelihara kelas yang diharapkan untuk memahami enkapsulasi OO yang diinginkan dan diberdayakan untuk mengoordinasikan implementasinya. Jadi, jika Anda menulis class X, Anda menyandikan tidak hanya bagaimana sebuah X xobjek dapat digunakan oleh kode yang memiliki akses ke sana, tetapi juga bagaimana:

  • kelas turunan dapat berinteraksi dengannya (melalui fungsi virtual opsional-murni dan / atau akses terlindungi), dan
  • Xobjek yang berbeda bekerja sama untuk memberikan perilaku yang dimaksudkan sambil menghormati kondisi pasca dan invarian dari desain Anda.

Ini bukan hanya konstruktor salinan - banyak sekali operasi yang dapat melibatkan dua atau lebih contoh kelas Anda: jika Anda membandingkan, menambah / mengalikan / membagi, menyalin-membangun, mengkloning, menetapkan, dll. Maka sering kali Anda baik hanya harus memiliki akses ke data pribadi dan / atau dilindungi di objek lain, atau ingin memungkinkan implementasi fungsi yang lebih sederhana, lebih cepat, atau secara umum lebih baik.

Secara khusus, operasi ini mungkin ingin memanfaatkan akses pribadi untuk melakukan hal-hal seperti:

  • (menyalin konstruktor) menggunakan anggota pribadi dari objek "rhs" (sisi kanan) dalam daftar penginisialisasi, sehingga variabel anggota itu sendiri dibuat-salinan alih-alih dibangun secara default (jika legal) kemudian ditetapkan juga (sekali lagi, jika legal)
  • berbagi sumber daya - pegangan file, segmen memori bersama, shared_ptrke data referensi, dll.
  • mengambil kepemilikan atas sesuatu, misalnya auto_ptr<>"memindahkan" kepemilikan ke objek yang sedang dibangun
  • salin "cache" pribadi, kalibrasi, atau anggota status yang diperlukan untuk membuat objek baru dalam keadaan yang dapat digunakan secara optimal tanpa harus membuat ulang dari awal
  • menyalin / mengakses diagnostik / melacak informasi yang disimpan dalam objek yang sedang disalin yang tidak dapat diakses melalui API publik tetapi mungkin digunakan oleh beberapa objek pengecualian atau pencatatan nanti (misalnya sesuatu tentang waktu / keadaan ketika instance "asli" yang tidak dibuat salinannya dibangun)
  • melakukan penyalinan yang lebih efisien dari beberapa data: misalnya objek mungkin memiliki misalnya unordered_mapanggota tetapi secara publik hanya mengekspos begin()dan end()iterator - dengan akses langsung ke size()Anda dapat reservekapasitas untuk menyalin lebih cepat; lebih buruk lagi jika mereka hanya mengekspos at()dan insert()dan sebaliknya throw....
  • salin referensi kembali ke objek induk / koordinasi / manajemen yang mungkin tidak diketahui atau hanya-tulis untuk kode klien
Tony Delroy
sumber
2
Saya pikir 'mengapa' terbesar adalah bahwa itu akan menjadi overhead runtime yang luar biasa untuk memeriksa apakah this == othersetiap kali Anda mengakses other.xyang harus Anda lakukan jika pengubah akses bekerja pada tingkat objek.
aioobe
2
@aioobe Saya pikir jawaban Anda harus jauh, jauh lebih menonjol. Sementara jawaban Tony benar-benar bagus dan konseptual, jika saya seorang penjudi, saya berani bertaruh bahwa jawaban Anda adalah alasan historis sebenarnya untuk pilihan tersebut. Tidak hanya lebih berkinerja, tetapi juga lebih sederhana. Akan menjadi pertanyaan yang bagus untuk Bjarne!
Nir Friedman
Saya telah menandai jawaban Anda, karena menjelaskan latar belakangnya;)
demonking
@demonking, saya pikir alasan yang diberikan dalam jawaban ini mencakup mengapa nyaman membiarkan data pribadi terbuka untuk objek lain. Tetapi pengubah akses tidak dimaksudkan untuk membuat data cukup "terbuka". Mereka lebih dimaksudkan untuk membuat data cukup tertutup untuk enkapsulasi. (Dalam hal kenyamanan , akan lebih baik jika variabel privat di mana publik!) Saya memperbarui jawaban saya dengan bagian yang menurut saya lebih baik membahas alasan sebenarnya .
aioobe
@aioobe: komentar lama, tapi bagaimanapun ... "periksa apakah this == othersetiap kali Anda mengakses other.x" - meleset dari intinya - jika other.xhanya diterima pada waktu proses jika setara dengan this.x, tidak akan ada banyak penulisan pointer other.xdi tempat pertama; kompiler mungkin juga memaksa Anda untuk menulis if (this == other) ...this.x...apa pun yang akan Anda lakukan. Anda "kenyamanan (bahkan lebih jika variabel swasta publik)" konsepsi juga melenceng - cara didefinisikan cukup ketat Standar untuk memungkinkan tepat enkapsulasi, tetapi tidak perlu merepotkan.
Tony Delroy
109

Pengubah akses bekerja pada tingkat kelas , dan bukan pada tingkat objek .

Artinya, dua objek dari kelas yang sama dapat mengakses data pribadi satu sama lain.

Mengapa:

Terutama karena efisiensi. Ini akan menjadi overhead this == otherwaktu proses yang tidak dapat diabaikan untuk memeriksa apakah setiap kali Anda mengakses other.xyang harus Anda lakukan jika pengubah akses bekerja pada tingkat objek.

Ini juga agak logis secara semantik jika Anda memikirkannya dalam istilah pelingkupan: "Seberapa besar bagian kode yang perlu saya ingat saat memodifikasi variabel privat?" - Anda perlu mengingat kode seluruh kelas, dan ini ortogonal objek yang ada saat runtime.

Dan sangat nyaman saat menulis pembuat salinan dan operator penugasan.

aioobe
sumber
35

Anda dapat mengakses anggota privat kelas dari dalam kelas tersebut, bahkan dari instance lain.

Alexander Rafferty
sumber
10

Untuk memahami jawabannya, saya ingin mengingatkan Anda beberapa konsep.

  1. Tidak peduli berapa banyak objek yang Anda buat, hanya ada satu salinan dari satu fungsi dalam memori untuk kelas itu. Artinya fungsi dibuat hanya sekali. Namun variabel terpisah untuk setiap instance kelas.
  2. this pointer diteruskan ke setiap fungsi saat dipanggil.

Sekarang karena thispenunjuknya, fungsi dapat menemukan variabel dari contoh tertentu itu. tidak peduli apakah itu pribadi untuk umum. itu dapat diakses di dalam fungsi itu. Sekarang jika kita meneruskan pointer ke objek lain dari kelas yang sama. menggunakan penunjuk kedua ini kita akan dapat mengakses anggota pribadi.

Semoga ini menjawab pertanyaan Anda.

Ali Zaib
sumber
6

Copy konstruktor adalah fungsi anggota kelas dan dengan demikian memiliki akses ke anggota data kelas, bahkan yang dideklarasikan sebagai 'pribadi'.

Bojan Komazec
sumber
4
Sebagai seseorang yang mengerti bahasanya, saya mengerti apa yang Anda maksud. Namun, jika saya tidak tahu bahasanya, saya akan mengira Anda hanya mengulangi pertanyaan itu kembali kepada saya.
San Jacinto