Почему объекты одного класса имеют доступ к личным данным друг друга?

99

Почему объекты одного класса имеют доступ к личным данным друг друга?

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

Этот код работает. Для объекта a вполне возможно получить доступ к личным данным из объекта b и вернуть их. Почему это должно быть так? Я бы подумал, что личные данные являются личными. (Я начал с попытки понять конструкторы копирования в идиоме pimpl, но затем обнаружил, что даже не понимаю эту простую ситуацию.)

Кит
источник
18
Ну, в качестве отправной точки вы не сможете правильно реализовать какие-либо копирующие конструкторы для чего-либо, кроме простейших классов. Вы можете думать о классах как о своих лучших друзьях :-)
Кэмерон
4
Подумайте о частном от своих клиентов, но все сотрудники класса имеют доступ
Мартин Беккет
Спасибо, Кэмерон. В этом есть смысл, но тогда почему этот доступ не ограничен только конструкторами копирования и операторами присваивания?
Кит
5
Объекты одного типа часто много взаимодействуют. И никто не заставляет вас писать метод, который раздает личные данные другого экземпляра. :)
UncleBens 03
4
Просто потому, что во время компиляции компилятор не имеет возможности идентифицировать тот же объект. Обеспечение такого доступа потребует поддержки во время выполнения.
Четан

Ответы:

80

Потому что именно так это работает в C ++. В C ++ контроль доступа работает для каждого класса , а не для каждого объекта.

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

Некоторые намеки на управление отдельными объектами присутствуют в спецификации защищенного доступа , поэтому в стандарте есть отдельная глава (11.5). Но все же любые описанные здесь индивидуальные особенности довольно элементарны. Опять же, контроль доступа в C ++ предназначен для работы на уровне каждого класса.

Муравей
источник
9
+1. С ++ хорошо разбирается в механизмах времени компиляции, но не в механизмах времени выполнения. Довольно хорошее общее правило.
Nemo
4
Ваше «на самом деле невозможно реализовать какой-либо значимый контроль доступа для каждого объекта во время компиляции». Почему нет? В void X::f(X&x)языке компилятор легко распознает this->aи x.a. Компилятор не (всегда) может знать, что *thisи xна самом деле являются одним и тем же объектом, если x.f(x)он вызывается, но я мог очень хорошо видеть, что дизайнер языка нашел это нормально.
Андре Карон
@ AndréCaron Я думаю, что это на самом деле гораздо больший котел с рыбой, чем ты представляешь. Если встраивание не происходит, компилятор всегда должен проверять, совпадают ли thisи &xявляются ли они одинаковыми. Что еще хуже, это на самом деле становится проблемой даже X::f(Y& y), потому что наш конкретный объект может иметь тип, Zкоторый наследуется от обоих Xи Y. Короче говоря, это настоящий беспорядок, неэффективный, сложно заставить работать с MI разумно.
Нир Фридман
@NirFriedman Я думаю, вы неправильно поняли предложение. При компиляции X::f(X& x), если есть доступ x.a, он не компилируется. Больше ничего не меняется, никаких проверок вставлять не нужно, поэтому на производительность еще действующих программ не влияет. И это предлагается не как критическое изменение существующего C ++, а как нечто, что дизайнеры могли бы сделать при privateпервоначальном внедрении .
Алексей Романов
31

«Частный» на самом деле не является механизмом контроля доступа в смысле «Я сделал свои фотографии на facebook частными, чтобы вы не могли их видеть».

В C ++ «частный» просто означает, что это части класса, которые вы (кодировщик класса) можете изменить в будущих версиях и т. Д., И вы не хотите, чтобы другие кодировщики, использующие ваш класс, полагались на их существование или функциональность. .

Если вам нужен настоящий контроль доступа, вам следует реализовать настоящие методы защиты данных.

всехар
источник
13

Это хороший вопрос, и я недавно столкнулся с ним. Я провел несколько дискуссий с моими коллегами, и вот краткое изложение нашего обсуждения: Это задумано. Это не означает, что этот дизайн полностью приемлем для всех случаев, но должны быть некоторые соображения, почему для каждого класса выбирается частный. Возможные причины, о которых мы могли бы подумать, включают:

Во-первых, стоимость управления доступом к экземпляру может быть очень высокой. Это обсуждалось другими в этой ветке. Теоретически это можно сделать с помощью этого проверки указателя. Однако это нельзя сделать во время компиляции, а можно сделать только во время выполнения. Таким образом, вы должны идентифицировать контроль доступа каждого члена во время выполнения, и когда он нарушается, возможно, будут возникать только исключения. Стоимость высока.

Во-вторых, управление доступом для каждого класса имеет собственный вариант использования, например конструктор копирования или оператор =. Их было бы сложно реализовать, если бы контроль доступа был индивидуальным.

Кроме того, контроль доступа осуществляется в основном с точки зрения программирования / языка, с точки зрения того, как модулировать / контролировать доступ к коду / члену, а не к данным.

ДжекиЖу
источник
12

Это в некоторой степени произвольное дизайнерское решение языка. В Ruby , например, это privateдействительно означает частный, поскольку в «только экземпляр может получить доступ к своим собственным частным элементам данных». Однако это несколько ограничительно.

Как указано в комментариях, конструкторы копирования и операторы присваивания - это обычные места, где вы напрямую получаете доступ к частным членам данных другого экземпляра. Есть менее очевидные причины.

Рассмотрим следующий случай. Вы реализуете объектно-ориентированный связанный список. Связанный список имеет вложенный класс узлов для управления указателями. Вы можете реализовать этот класс узла так, чтобы он сам управлял указателями (а не делал указатели общедоступными и управлялись списком). В таком случае у вас были бы объекты узлов, которые хотели бы изменить указатели других узловых объектов в других местах, где используется типичный конструктор копирования и оператор присваивания.

Андре Карон
источник
4

Уловка заключается в том, чтобы помнить, что данные относятся privateк классу , а не к экземпляру класса. Любой метод в вашем классе может получить доступ к закрытым данным любого экземпляра этого класса; не существует способа сохранить конфиденциальность данных внутри экземпляра, если только вы не запретите методы, которые явно обращаются к частным элементам данных других экземпляров.

Адам Марас
источник
1

В дополнение ко всем вышеперечисленным ответам рассмотрите настраиваемые конструкторы копирования, операторы присваивания и все другие функции, которые вы бы написали для класса, который работает с другими экземплярами . Для всех этих членов данных вам потребуются функции доступа.

Джейкоб
источник
-8

Личные данные остаются конфиденциальными до тех пор, пока кто-то, имеющий к ним доступ, не раскроет их другим.

Эта концепция применима и к другой ситуации, например:

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

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

private:
   int iTheSecretVault;
};

Как можно было снять деньги? :)

YeenFei
источник
3
В этом примере один экземпляр класса не обращается к частным элементам данных другого экземпляра.
Андре Карон
@Andre, «Эта концепция применима и к другой ситуации, например ...»
YeenFei
^ «другая ситуация» не по теме по определению, поэтому ваш пример не имеет отношения (и я не уверен, что он был бы информативным где-либо еще)
underscore_d
1
Это совсем не правильное объяснение вопроса.
Panda