Dlaczego obiekty tej samej klasy mają dostęp do swoich prywatnych danych?

99

Dlaczego obiekty tej samej klasy mają dostęp do swoich prywatnych danych?

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

Ten kod działa. Obiekt a może uzyskać dostęp do prywatnych danych z obiektu b i je zwrócić. Dlaczego tak się dzieje? Myślę, że prywatne dane są prywatne. (Zacząłem od zrozumienia konstruktorów kopiujących w idiomie pimpl, ale potem odkryłem, że nawet nie rozumiem tej prostej sytuacji).

Keith
źródło
18
Cóż, na początek nie byłbyś w stanie poprawnie zaimplementować żadnych konstruktorów kopiujących dla niczego poza najprostszymi klasami. Możesz myśleć o zajęciach jak o ich najlepszym przyjacielu :-)
Cameron
4
Pomyśl o prywatnym od swoich klientów, ale wszyscy pracownicy klasy mają dostęp
Martin Beckett
Dzięki Cameron. Ma to sens, ale dlaczego ten dostęp nie jest ograniczony tylko do konstruktorów kopiujących i operatorów przypisania?
Keith
5
Obiekty tego samego typu często wchodzą w interakcje. I nikt nie zmusza cię do napisania metody, która przekazuje prywatne dane innej instancji. :)
UncleBens
4
Po prostu dlatego, że w czasie kompilacji kompilator nie ma możliwości zidentyfikowania tego samego obiektu. Wymuszenie takiego dostępu wymagałoby wsparcia w czasie wykonywania.
Chethan

Odpowiedzi:

80

Ponieważ tak to działa w C ++. W C ++ kontrola dostępu działa na podstawie klasy , a nie obiektu.

Kontrola dostępu w C ++ jest implementowana jako statyczna funkcja czasu kompilacji. Myślę, że jest raczej oczywiste, że nie jest naprawdę możliwe zaimplementowanie jakiejkolwiek znaczącej kontroli dostępu dla poszczególnych obiektów w czasie kompilacji. W ten sposób można zaimplementować tylko kontrolę dla klasy.

Pewne wskazówki dotyczące kontroli poszczególnych obiektów są obecne w specyfikacji chronionego dostępu , dlatego też ma ona nawet własny, dedykowany rozdział w standardzie (11.5). Ale nadal wszelkie opisane cechy dla poszczególnych obiektów są raczej podstawowe. Ponownie, kontrola dostępu w C ++ ma działać na podstawie klasy.

Mrówka
źródło
9
+1. C ++ zajmuje się mechanizmami czasu kompilacji, a nie mechanizmami czasu wykonywania. Całkiem dobra ogólna zasada.
Nemo
4
Twoje „nie jest naprawdę możliwe zaimplementowanie jakiejkolwiek znaczącej kontroli dostępu dla poszczególnych obiektów w czasie kompilacji”. Dlaczego nie? W void X::f(X&x)programie kompilator jest w stanie łatwo odróżnić this->ai x.a. To nie jest (zawsze) możliwe kompilator wiedział, że *thisi xsą w rzeczywistości ten sam przedmiot, jeśli x.f(x)jest wywoływany, ale mogę bardzo dobrze zobaczyć projektant język odnaleźć tę OK.
André Caron
@ AndréCaron Myślę, że w rzeczywistości jest to o wiele większy kociołek z rybami, niż ci się wydaje. Gdy inlining nie występuje, kompilator zawsze będzie musiał sprawdzić, czy thisi &xsą takie same. Co gorsza, w rzeczywistości kończy się to problemem X::f(Y& y), ponieważ nasz konkretny obiekt może być typu, Zktóry dziedziczy zarówno z, jak Xi Y. Krótko mówiąc, to prawdziwy bałagan, a nie wydajność, ciężko jest rozsądnie pracować z MI.
Nir Friedman
@NirFriedman Myślę, że źle zrozumiałeś sugestię. Podczas kompilacji X::f(X& x), jeśli są dostęp do x.a, nie będzie się kompilować. Nic więcej się nie zmienia, nie trzeba wstawiać żadnych sprawdzeń, więc nie ma to wpływu na działanie nadal działających programów. I nie jest to sugerowane jako przełomowa zmiana w istniejącym C ++, ale jako coś, co projektanci mogliby zrobić, wprowadzając privatepierwotnie.
Alexey Romanov
31

„Prywatne” nie jest tak naprawdę mechanizmem kontroli dostępu w sensie „Zrobiłem moje zdjęcia na Facebooku jako prywatne, więc nie możesz ich zobaczyć”.

W C ++ „private” mówi po prostu, że są to części klasy, które Ty (programista tej klasy) możesz zmienić w przyszłych wersjach itp. I nie chcesz, aby inni programiści używający Twojej klasy polegali na ich istnieniu lub funkcjonalności .

Jeśli chcesz mieć prawdziwą kontrolę dostępu, powinieneś wdrożyć oryginalne techniki bezpieczeństwa danych.

vsekhar
źródło
13

To dobre pytanie i ostatnio zetknąłem się z tym pytaniem. Odbyłem kilka dyskusji z moimi kolegami i oto podsumowanie naszej dyskusji: To jest zgodne z projektem. Nie oznacza to, że ten projekt jest całkowicie rozsądny we wszystkich przypadkach, ale należy wziąć pod uwagę, dlaczego wybrana jest klasa prywatna. Możliwe powody, o których moglibyśmy pomyśleć, obejmują:

Przede wszystkim koszt kontroli dostępu do instancji może być bardzo wysoki. Zostało to omówione przez innych w tym wątku. Teoretycznie można to zrobić za pomocą tego sprawdzenia wskaźnika. Nie można tego jednak zrobić w czasie kompilacji i można to zrobić tylko w czasie wykonywania. Musisz więc zidentyfikować kontrolę dostępu każdego członka w czasie wykonywania, a kiedy zostanie naruszona, prawdopodobnie zostaną zgłoszone tylko wyjątki. Koszt jest wysoki.

Po drugie, kontrola dostępu dla klasy ma swój własny przypadek użycia, taki jak konstruktor kopiujący lub operator =. Trudno byłoby je wdrożyć, gdyby kontrola dostępu dotyczyła instancji.

Ponadto kontrola dostępu odbywa się głównie z perspektywy programowania / języka, w zakresie modularyzacji / kontroli dostępu do kodu / elementu członkowskiego, a nie danych.

JackyZhu
źródło
12

To trochę arbitralna decyzja dotycząca projektu językowego. Na przykład w Rubimprivate naprawdę oznacza „prywatny”, ponieważ „tylko instancja ma dostęp do swoich prywatnych członków danych”. Jest to jednak nieco restrykcyjne.

Jak wskazano w komentarzach, konstruktory kopiujące i operatory przypisania są typowymi miejscami, w których uzyskujesz bezpośredni dostęp do prywatnych danych członków innej instancji. Istnieją mniej oczywiste powody.

Rozważ następujący przypadek. Implementujesz listę połączoną OO. Lista połączona zawiera zagnieżdżoną klasę węzłów do zarządzania wskaźnikami. Możesz zaimplementować tę klasę węzła w taki sposób, że sama zarządza wskaźnikami (zamiast publicznych i zarządzanych przez listę wskaźników). W takim przypadku obiekty węzłów chciałyby zmodyfikować wskaźniki innych obiektów węzłów w innych miejscach niż typowy konstruktor kopiujący i operator przypisania.

André Caron
źródło
4

Sztuką jest, aby pamiętać, że dane są privatedo klasy , a nie instancji klasy. Każda metoda w Twojej klasie może uzyskać dostęp do prywatnych danych dowolnej instancji tej klasy; nie ma sposobu, aby zachować prywatność danych wewnątrz instancji, chyba że zabronisz metod, które jawnie uzyskują dostęp do prywatnych danych członków innych instancji.

Adam Maras
źródło
1

Oprócz wszystkich powyższych odpowiedzi, rozważ niestandardowe konstruktory kopiowania, operatory przypisania i wszystkie inne funkcje, które napisałbyś dla klasy, które działają na innych instancjach . Potrzebujesz funkcji akcesorów dla wszystkich tych członków danych.

Jakub
źródło
-8

Dane prywatne pozostają prywatne, dopóki ktoś, kto ma do nich dostęp, nie ujawni ich innym.

Ta koncepcja dotyczy również innych sytuacji, takich jak:

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

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

private:
   int iTheSecretVault;
};

Jak ktoś mógł wypłacić pieniądze? :)

YeenFei
źródło
3
Ten przykład nie obejmuje jednej instancji klasy, która uzyskuje dostęp do prywatnych członków danych innej instancji.
André Caron
@Andre, „Ta koncepcja odnosi się również do innych sytuacji, takich jak ...”
YeenFei,
^ „inna sytuacja” jest z definicji niezwiązana z tematem, więc Twój przykład nie ma znaczenia (i nie jestem pewien, czy nigdzie indziej miałby znaczenie)
podkreślenie_d
1
To wcale nie jest poprawne wyjaśnienie pytania.
Panda