¿Por qué los objetos de la misma clase tienen acceso a los datos privados de los demás?

98

¿Por qué los objetos de la misma clase tienen acceso a los datos privados de los demás?

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

Este código funciona. Es perfectamente posible que el objeto a acceda a datos privados del objeto by los devuelva. ¿Por qué esto es así? Creo que los datos privados son privados. (Comencé tratando de entender los constructores de copias en el idioma pimpl, pero luego descubrí que ni siquiera entendía esta simple situación).

Keith
fuente
18
Bueno, como punto de partida, no podría implementar correctamente ningún constructor de copia para nada más que las clases más simples. Puedes pensar en las clases como su propio mejor amigo :-)
Cameron
4
Piense en privado de sus clientes, pero todos los empleados de la clase tienen acceso
Martin Beckett
Gracias Cameron. Eso tiene sentido, pero entonces, ¿por qué este acceso no está restringido solo a los constructores de copias y los operadores de asignación?
Keith
5
Los objetos del mismo tipo suelen interactuar mucho. Y nadie te está obligando a escribir un método que distribuya datos privados de otra instancia. :)
UncleBens
4
Simplemente porque, en el momento de la compilación, el compilador no tiene forma de identificar el mismo objeto. Hacer cumplir dicho acceso requeriría soporte en tiempo de ejecución.
Chethan

Respuestas:

80

Porque así es como funciona en C ++. En C ++, el control de acceso funciona por clase , no por objeto.

El control de acceso en C ++ se implementa como una característica estática en tiempo de compilación. Creo que es bastante obvio que no es realmente posible implementar ningún control de acceso significativo por objeto en tiempo de compilación. Solo el control por clase se puede implementar de esa manera.

Algunas sugerencias de control por objeto están presentes en la especificación de acceso protegido , por lo que incluso tiene su propio capítulo dedicado en el estándar (11.5). Pero aún así, las características por objeto descritas allí son bastante rudimentarias. Nuevamente, el control de acceso en C ++ está diseñado para funcionar por clase.

Hormiga
fuente
9
+1. C ++ es grande en mecanismos de tiempo de compilación, no tan grande en mecanismos de tiempo de ejecución. Regla general bastante buena.
Nemo
4
Su "no es realmente posible implementar ningún control de acceso significativo por objeto en tiempo de compilación". Por qué no? En void X::f(X&x), el compilador es fácilmente capaz de distinguir this->ay x.a. No es (siempre) posible que el compilador sepa eso *thisy en xrealidad son el mismo objeto si x.f(x)se invoca, pero podría ver que un diseñador de lenguaje lo encuentra bien.
André Caron
@ AndréCaron Creo que esto es en realidad una olla de pescado mucho más grande de lo que imaginas. Cuando no se produce la inserción, el compilador siempre tendrá que comprobar si thisy &xson iguales. Para empeorar las cosas, esto acaba siendo un problema incluso con X::f(Y& y), porque nuestro objeto concreto podría ser de un tipo Zque herede de ambos Xy Y. En resumen, es un verdadero desastre, no funciona, es difícil hacer que funcione con sensatez con MI.
Nir Friedman
@NirFriedman Creo que malinterpretas la sugerencia. Al compilar X::f(X& x), si hay accesos a x.a, no compilaría. Nada más cambia, no es necesario insertar comprobaciones, por lo que el rendimiento de los programas aún válidos no se ve afectado. Y no se sugiere como un cambio radical en C ++ existente, sino como algo que los diseñadores podrían haber hecho al presentarlo privateoriginalmente.
Alexey Romanov
31

"Privado" no es realmente un mecanismo de control de acceso en el sentido de "Hice mis fotos en Facebook privadas para que no puedas verlas".

En C ++, "privado" simplemente dice que estas son partes de una clase que usted (el codificador de la clase) podría cambiar en versiones futuras, etc., y no quiere que otros codificadores que usen su clase dependan de su existencia o funcionalidad. .

Si desea un verdadero control de acceso, debe implementar técnicas de seguridad de datos genuinas.

vsekhar
fuente
13

Esta es una buena pregunta y me he encontrado con esta pregunta recientemente. Tuve algunas discusiones con mis colegas y aquí está el resumen de nuestra discusión: Esto es por diseño. No significa que este diseño sea totalmente razonable para todos los casos, pero debe haber algunas consideraciones sobre por qué se elige por clase privada. Las posibles razones que podríamos pensar incluyen:

En primer lugar, el costo del control de acceso por instancia podría ser muy alto. Esto ha sido discutido por otros en este hilo. En teoría, esto se puede hacer a través de esta verificación de puntero. Sin embargo, esto no se puede hacer en tiempo de compilación y solo se puede hacer en tiempo de ejecución. Por lo tanto, debe identificar el control de acceso de cada miembro en el tiempo de ejecución, y cuando se infringe, posiblemente solo se generarán excepciones. El costo es alto.

En segundo lugar, el control de acceso por clase tiene su propio caso de uso, como constructor de copias o operador =. Sería difícil implementarlos si el control de acceso es por instancia.

Además, el control de acceso es principalmente desde la perspectiva del lenguaje / programación, de cómo modularizar / controlar el acceso al código / miembro, no a los datos.

JackyZhu
fuente
12

Es una decisión de diseño de lenguaje algo arbitraria. En Ruby , por ejemplo, privaterealmente significa privado, como en "solo la instancia puede acceder a sus propios miembros de datos privados". Sin embargo, esto es algo restrictivo.

Como se señaló en los comentarios, los constructores de copia y los operadores de asignación son lugares comunes donde se accede directamente a los miembros de datos privados de otra instancia. Hay razones menos obvias para ello.

Considere el siguiente caso. Está implementando una lista enlazada OO. La lista vinculada tiene una clase de nodo anidado para administrar punteros. Puede implementar esta clase de nodo de modo que administre los punteros en sí (en lugar de tener los punteros públicos y administrados por la lista). En tal caso, los objetos de nodo querrían modificar los punteros de otros objetos de nodo en otros lugares que el constructor de copia típico y el operador de asignación.

André Caron
fuente
4

El truco consiste en recordar que los datos pertenecen privatea la clase , no a la instancia de la clase. Cualquier método dentro de su clase puede acceder a los datos privados de cualquier instancia de esa clase; No hay forma de mantener la privacidad de los datos dentro de una instancia a menos que prohíba los métodos que acceden explícitamente a los miembros de datos privados de otras instancias.

Adam Maras
fuente
1

Además de todas las respuestas anteriores, considere los constructores de copia personalizados, los operadores de asignación y todas las demás funciones que escribiría para una clase que opere en otras instancias . Necesitaría funciones de acceso para todos esos miembros de datos.

Jacob
fuente
-8

Los datos privados permanecen privados hasta que alguien que tenga acceso a ellos se los revele a otros.

Este concepto también se aplica a otras situaciones, como:

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

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

private:
   int iTheSecretVault;
};

¿Cómo podría alguien retirar el dinero? :)

YeenFei
fuente
3
Este ejemplo no implica que una instancia de clase acceda a los miembros de datos privados de otra instancia.
André Caron
@Andre, "Este concepto también se aplica a otras situaciones, como ..."
YeenFei
^ "otra situación" está fuera de tema por definición, por lo que su ejemplo no es relevante (y no estoy seguro de que sea informativo en ningún otro lugar)
underscore_d
1
Esta no es una explicación correcta a la pregunta.
Panda