Почему я могу получить доступ к закрытым переменным в конструкторе копирования?

Я узнал, что я никогда не могу получить доступ к частной переменной, только с функцией 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;

person demonking    schedule 07.11.2010    source источник
comment
Потому что конструктор копирования является членом класса по умолчанию, как и некоторые другие.   -  person DumbCoder    schedule 09.11.2010
comment
+53/-0? Кто голосовал за это? Как еще вы могли бы их скопировать?!? (Давайте разоблачим не-альтернативы: создайте общедоступный геттер ссылок для каждого частного члена? Тогда они вообще не будут частными. Создайте общедоступный геттер const& или по значению для каждого? Тогда они будут только «закрытыми для записи», и для значений тратят ресурсы впустую и терпят неудачу для некопируемых членов.) Я сбит с толку таким успехом такого бессодержательного вопроса, спрашивая о конструкции копирования, полностью игнорируя, что это значит, и ни один ответ не использует базовую логику, чтобы опровергнуть это. Они объясняют сухие технические детали, но есть гораздо более простой ответ на этот зашоренный вопрос.   -  person underscore_d    schedule 16.08.2016
comment
@underscore_d, а как еще их копировать? очень странный ответ на мой взгляд. Это как ответить на вопрос, как работает гравитация? с как иначе вещи упали бы вниз! На самом деле довольно часто путают инкапсуляцию на уровне класса с инкапсуляцией на уровне объекта. Забавно, как вы, кажется, думаете, что это глупый вопрос и что ответ должен быть очевидным. Имейте в виду, что инкапсуляция в Smalltalk (вероятно, архетипическом объектно-ориентированном языке) фактически работает на уровне объектов.   -  person aioobe    schedule 21.11.2016
comment
@aioobe Хороший вопрос, спасибо. Мой комментарий довольно экстремальный - возможно, в тот день сломалась кофемашина. Я ценю, что вы указали, почему этот вопрос будет популярен, особенно среди тех, кто родом из других (и, возможно, больше) языков OO. На самом деле, можно утверждать, что мой комментарий был зашоренным, поскольку я писал с точки зрения человека, который в основном программирует на C++. Кроме того, мне нравится эта аналогия с гравитацией!   -  person underscore_d    schedule 21.11.2016


Ответы (5)


ИМХО, существующие ответы плохо объясняют «почему» этого - слишком много внимания уделяется повторению того, какое поведение действительно. «модификаторы доступа работают на уровне класса, а не на уровне объекта». - Да, но почему?

Общая концепция здесь заключается в том, что программисты, разрабатывающие, пишущие и поддерживающие класс, должны понимать желаемую объектно-ориентированную инкапсуляцию и уполномочены координировать ее реализацию. Итак, если вы пишете class X, вы кодируете не только то, как отдельный объект X x может использоваться кодом с доступом к нему, но и то, как:

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

Это не просто конструктор копирования — очень многие операции могут включать два или более экземпляров вашего класса: если вы сравниваете, складываете/умножаете/делите, копируете-конструируете, клонируете, присваиваете и т. д., то часто бывает так, что вы либо просто должен иметь доступ к закрытым и/или защищенным данным в другом объекте, либо хочет, чтобы он допускал более простую, быструю или в целом лучшую реализацию функции.

В частности, эти операции могут использовать преимущества привилегированного доступа для выполнения таких действий, как:

  • (конструкторы копирования) используют закрытый член объекта "rhs" (правая сторона) в списке инициализаторов, так что переменная-член сама создается путем копирования, а не по умолчанию (если даже допустимо), а затем также назначается (опять же, если законно)
  • совместно использовать ресурсы - дескрипторы файлов, сегменты общей памяти, shared_ptrs для ссылки на данные и т. д.
  • взять на себя ответственность за вещи, т.е. auto_ptr<> «переходит» право собственности на строящийся объект
  • копировать частный «кеш», калибровку или члены состояния, необходимые для создания нового объекта в оптимально пригодном для использования состоянии, без необходимости регенерировать их с нуля
  • копировать/получать доступ к диагностической/трассировочной информации, хранящейся в копируемом объекте, которая иначе недоступна через общедоступные API, но может быть использована каким-либо более поздним объектом исключения или ведением журнала (например, что-то о времени/обстоятельствах, когда «исходный» экземпляр, созданный без копирования был построен)
  • выполнить более эффективную копию некоторых данных: например. объекты могут иметь, например. член unordered_map, но публично раскрывать только begin() и end() итераторы - с прямым доступом к size() вы можете reserve увеличить емкость для более быстрого копирования; еще хуже, если они разоблачают только at() и insert(), а иначе throw....
  • копировать ссылки обратно на родительские/координационные/управляющие объекты, которые могут быть неизвестны или доступны только для записи для клиентского кода
person Tony Delroy    schedule 18.07.2013
comment
Я думаю, что самое большое «почему» заключается в том, что было бы огромными накладными расходами во время выполнения проверять, this == other ли каждый раз, когда вы обращаетесь к other.x, что вам пришлось бы делать, если бы модификаторы доступа работали на уровне объекта. - person aioobe; 13.07.2016
comment
@aioobe Я думаю, что ваш ответ должен быть намного, намного более заметным. Хотя ответ Тони действительно хорош и концептуален, если бы я был игроком на ставках, я бы поспорил, что ваш ответ является фактической исторической причиной выбора. Это не только более производительно, но и намного проще. Отличный вопрос для Бьярна! - person Nir Friedman; 04.04.2017
comment
Я отметил ваш ответ, потому что он объясняет предысторию;) - person demonking; 29.06.2017
comment
@demonking, я думаю, причины, приведенные в этом ответе, объясняют, почему удобно открывать личные данные для других объектов. Но модификаторы доступа не предназначены для того, чтобы делать данные достаточно открытыми. Они скорее предназначены для того, чтобы сделать данные закрытыми достаточными для инкапсуляции. (С точки зрения удобства было бы даже лучше, если бы частные переменные были общедоступными!) Я обновил свой ответ разделом, который, как мне кажется, лучше описывает фактическое почему< /я>. - person aioobe; 30.06.2017
comment
@aioobe: старые комментарии, но в любом случае ... проверяйте, если this == other каждый раз, когда вы обращаетесь к other.x - упускает суть - если бы other.x принимался только во время выполнения, когда он эквивалентен this.x, не было бы много указателей, записывающих other.x в первую очередь; компилятор может также заставить вас писать if (this == other) ...this.x... для всего, что вы собираетесь делать. Ваша концепция удобства (даже больше, если закрытые переменные были общедоступными) также упускает из виду суть - способ определения Стандарта достаточно ограничителен, чтобы обеспечить правильную инкапсуляцию, но не является излишне неудобным. - person Tony Delroy; 06.02.2018

Модификаторы доступа работают на уровне класса, а не на уровне объекта.

То есть два объекта одного класса могут обращаться к закрытым данным друг друга.

Почему:

В первую очередь за счет эффективности. Проверка this == other каждый раз, когда вы обращаетесь к other.x, что вам пришлось бы делать, если бы модификаторы доступа работали на уровне объекта, потребовало бы немалых накладных расходов во время выполнения.

Это также семантически логично, если вы думаете об этом с точки зрения области видимости: «Какую большую часть кода мне нужно иметь в виду при изменении частной переменной?» — Вам нужно помнить о коде всего класса, и это ортогонально тому, какие объекты существуют во время выполнения.

И это невероятно удобно при написании конструкторов копирования и операторов присваивания.

person aioobe    schedule 07.11.2010

Вы можете получить доступ к закрытым членам класса из класса, даже к членам другого экземпляра.

person Alexander Rafferty    schedule 07.11.2010

Чтобы понять ответ, я хотел бы напомнить вам несколько понятий.

  1. Независимо от того, сколько объектов вы создаете, в памяти находится только одна копия одной функции для этого класса. Это означает, что функции создаются только один раз. Однако переменные являются отдельными для каждого экземпляра класса.
  2. Указатель this передается каждой функции при вызове.

Теперь благодаря указателю this функция может найти переменные этого конкретного экземпляра. независимо от того, является ли он частным или публичным. к нему можно получить доступ внутри этой функции. Теперь, если мы передаем указатель на другой объект того же класса. используя этот второй указатель, мы сможем получить доступ к закрытым членам.

Надеюсь, что это ответ на ваш вопрос.

person Ali Zaib    schedule 23.05.2012

Конструктор копирования является функцией-членом класса и поэтому имеет доступ к членам данных класса, даже к тем, которые объявлены как «частные».

person Bojan Komazec    schedule 07.11.2010
comment
Как человек, знающий язык, я понимаю, что вы имеете в виду. Однако, если бы я не знал языка, я бы подумал, что вы просто повторяете вопрос в ответ на мой вопрос. - person San Jacinto; 07.11.2010