Чому я можу отримати доступ до приватних змінних у конструкторі копіювання?


88

Я дізнався, що ніколи не можу отримати доступ до приватної змінної, лише за допомогою функції get у класі. Але чому тоді я можу отримати до нього доступ у конструкторі копіювання?

Приклад:

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);
}

Моя декларація:

private:
  T *pFirst,*pLast,*pEnd;

Оскільки конструктор копіювання за замовчуванням є членом класу, а також деякі інші.
DumbCoder

+ 53 / -0? Хто за це проголосував? Як би ви їх ще скопіювали ?!? (Давайте розвінчаємо неальтернативні варіанти: Створіть загальнодоступний довідковий геттер для кожного приватного учасника? Тоді вони взагалі не приватні. Зробити загальнодоступний const&або добутковий геттер для кожного? Тоді вони лише „писати-приватно“, & для цінностей витрачають ресурси і зазнають невдач для членів, які не підлягають копіюванню.) Мене бентежить такий успіх такого безглуздого питання, коли я запитую про конструювання копій, повністю ігноруючи, що це означає, і жодна відповідь не використовує базової логіки, щоб розвінчати це. Вони пояснюють сухі технічні аспекти, але є набагато простіша відповідь на запитання, яке це моргало
underscore_d

8
@underscore_d, "А як би інакше ви їх скопіювали?" на мій погляд, це дуже дивна відповідь. Це все одно, що відповісти "як працює гравітація?" з "як інакше все впаде!" Поплутати інкапсуляцію класу з інкапсуляцією об’єктного рівня насправді є досить поширеним явищем. Забавно, як ти здаєшся, що ти думаєш, що це дурне запитання і що відповідь має бути очевидною. Майте на увазі, що інкапсуляція у Smalltalk (можливо, архетипна мова OO) насправді працює на об’єктному рівні.
aioobe

@aioobe Гарна думка, дякую. Мій коментар є надзвичайно екстремальним - можливо, кавова машина була зламана того дня. Я вдячний Вам за вказівку, чому це питання було б популярним, особливо серед тих, хто походить з інших (і, можливо, більше) мов ОО. Насправді, можна сперечатися, що мій коментар був тим, що було "блимало", оскільки я писав з точки зору того, хто в основному програмує на C ++. Крім того, полюбіть цю аналогію гравітації!
underscore_d

Відповіді:


33

ІМХО, існуючі відповіді погано спрацьовують питання "чому" цього - занадто багато зосереджуючись на повторенні того, яка поведінка є дійсною. "модифікатори доступу працюють на рівні класу, а не на рівні об'єкта." - так, але чому?

Основною концепцією тут є те, що саме програміст (и) розробляє, пише та підтримує клас, який (як очікується) зрозуміє бажану інкапсуляцію ОО та наділений повноваженнями координувати її реалізацію. Отже, якщо ви пишете class X, ви кодуєте не просто те, як окремий X xоб’єкт може використовуватися кодом з доступом до нього, а й те, як:

  • похідні класи можуть взаємодіяти з ним (за вибором - чисто віртуальні функції та / або захищений доступ), і
  • окремі Xоб'єкти співпрацюють, щоб забезпечити передбачувану поведінку, одночасно дотримуючись пост-умов та інваріантів вашого дизайну.

Це не тільки конструктор копіювання - дуже багато операцій можуть включати два або більше екземплярів вашого класу: якщо ви порівнюєте, додаєте / множите / ділите, копіюєте, конструюєте, клонуєте, присвоюєте тощо, то часто трапляється так, що ви або просто повинен мати доступ до приватних та / або захищених даних в іншому об'єкті, або хотіти, щоб він дозволяв простішу, швидшу або, як правило, кращу реалізацію функції.

Зокрема, ці операції можуть захотіти скористатися привілейованим доступом, щоб робити такі речі:

  • (конструктори копіювання) використовують приватний член об'єкта "rhs" (праворуч) у списку ініціалізатора, так що змінна члена сама створюється за копією замість побудованої за замовчуванням (якщо навіть легальна), а потім теж призначається (знову ж якщо законно)
  • спільний доступ до ресурсів - дескриптори файлів, сегменти спільної пам'яті, shared_ptrs для посилань на дані тощо.
  • взяти у власність речі, наприклад, auto_ptr<>" перенести " право власності на об'єкт, що будується
  • копіювати приватний "кеш", калібрування або члени стану, необхідні для побудови нового об'єкта в оптимально зручному стані без необхідності їх регенерації з нуля
  • копіювати / отримувати інформацію про діагностику / трасування, що зберігається в об'єкті, який копіюється, інакше не доступний через загальнодоступні API, але може бути використаний якимсь пізнішим об'єктом виключення або реєстрацією (наприклад, щось про час / обставини, коли "оригінальний" екземпляр, створений без копіювання був побудований)
  • виконувати більш ефективну копію деяких даних: наприклад, об’єкти можуть мати, наприклад, unordered_mapчлена, але загальнодоступні лише викриття begin()та end()ітератори - з прямим доступом до size()вас може reserveбути швидшим копіюванням; ще гірше , якщо вони тільки викрити at()і insert()в іншому випадку throw....
  • копіювати посилання назад до батьківських / координаційних / керуючих об’єктів, які можуть бути невідомими або лише для запису для клієнтського коду

2
Я думаю, що найбільше «чому» - це надзвичайні накладні витрати на виконання, щоб перевірити, чи this == otherкожного разу, коли ви звертаєтесь до того, other.xщо вам потрібно було б робити, якщо модифікатори доступу працювали на рівні об’єкта.
aioobe

2
@aioobe Я думаю, що ваша відповідь повинна бути набагато видатнішою. Хоча відповідь Тоні справді хороша та концептуальна, якби я був людиною, яка б робила ставку, я б поспорився, що ваша відповідь є фактичною історичною причиною вибору. Це не тільки ефективніше, але і набагато простіше. Це було б чудовим питанням для Бьярна!
Нір Фрідман,

Я позначив вашу відповідь, оскільки вона пояснює передумови;)
демонстрація

@demonking, я думаю, причини, наведені у цій відповіді, охоплюють, чому зручно дозволити приватним даним бути відкритими для інших об’єктів. Але модифікатори доступу не мають на меті зробити дані достатньо «відкритими». Вони скоріше призначені для того, щоб зробити дані досить закритими для інкапсуляції. (З точки зору зручності було б ще краще, якщо приватні змінні є загальнодоступними!) Я оновив свою відповідь розділом, який, на мою думку, краще стосується фактичної причини .
aioobe

@aioobe: старі коментарі, але в будь-якому випадку ... "перевіряйте, чи this == otherкожен раз, коли ви отримуєте доступ other.x" - пропускає суть - якби це other.xбуло прийнято лише під час виконання, коли це еквівалентно, спочатку this.xне було б великої кількості вказівників other.x; компілятор також може змусити вас писати if (this == other) ...this.x...все, що ви збираєтеся робити. Ваша концепція "зручності (навіть більше, якщо приватні змінні були загальнодоступними)" також не вказує на суть - спосіб, визначений стандартом, є досить обмежувальним, щоб забезпечити належне інкапсулювання, але не надто незручним.
Тоні Делрой,

108

Модифікатори доступу працюють на рівні класу , а не на рівні об’єкта .

Тобто два об'єкти одного класу можуть отримувати доступ один до одного до приватних даних.

Чому:

В першу чергу завдяки ефективності. Це було б незначною накладними витратами на виконання, щоб перевірити, чи this == otherкожного разу, коли ви звертаєтесь до того, other.xщо вам потрібно було б, якщо модифікатори доступу працювали на об’єктному рівні.

Це також якось семантично логічно, якщо ви думаєте про це з точки зору масштабу: "Яку велику частину коду мені потрібно мати на увазі, змінюючи приватну змінну?" - Вам потрібно пам’ятати про код усього класу, і це ортогонально об’єкту, який існує під час виконання.

І це неймовірно зручно при написанні конструкторів копіювання та операторів присвоювання.



10

Щоб зрозуміти відповідь, я хотів би нагадати вам декілька понять.

  1. Незалежно від того, скільки об’єктів ви створюєте, в пам’яті для цього класу є лише одна копія однієї функції. Це означає, що функції створюються лише один раз. Однак змінні є окремими для кожного екземпляра класу.
  2. this покажчик передається кожній функції при виклику.

Тепер завдяки thisвказівнику функція може знаходити змінні цього конкретного екземпляра. незалежно від того, приватна вона для громадськості. до нього можна отримати доступ всередині цієї функції. Тепер, якщо ми передаємо вказівник на інший об'єкт того самого класу. використовуючи цей другий покажчик, ми зможемо отримати доступ до приватних членів.

Сподіваюся, це відповідає на ваше запитання.


6

Конструктор копіювання є функцією-членом класу і як такий має доступ до членів даних класу, навіть тих, які оголошені як "приватні".


3
Як людина, яка знає мову, я розумію, що ви маєте на увазі. Однак, якби я не знав мови, я міг би подумати, що ти просто повторюєш мені запитання.
Сан-Хасінто,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.