C++ FAQ

Esta es una recopilación de preguntas frecuentes (FAQ por sus siglas en inglés) publicado en C++ Super-FAQ pero no es una traducción oficial ni muy afinada aún. A medida que se avance con la traducción iremos publicando las otras secciones, ahora mismo estamos en la sección de clases y herencia.

Clases y Objetos

¿Qué es una clase?

Una clase es un elemento fundamental de la Programación Orientada a Objetos.

Una clase define un tipo de dato, muy parecido a una estructura (struct) en C. En informática, un tipo de dato se compone de estados y operaciones, y por su puesto puede haber transición entre estados. Así int es un tipo de dato porque tiene un conjunto de estados y operaciones como i + j o i++, etc. Exactamente de la misma manera, una clase proporciona un conjunto de (por lo general públicos) operaciones, y un conjunto de (por lo general no público) datos (también llamado propiedades) que representan los valores abstractos que las instancias de la clase pueden tener.

Usted puede imaginar que int es una clase que tiene una función miembro llamado operador++, etc (int en realidad no es una clase, pero la analogía básica es la siguiente:. una clase es un tipo de dato, al igual que int es un tipo de dato)

Nota: un programador de C puede pensar en una clase es como una estructura C, cuyos miembros por defecto son privados. Pero si eso es todo lo que piensa de una clase, entonces es probable que tenga que experimentar un cambio de paradigma personal.

¿Qué es un objeto?

Un objeto se define como la unidad que en tiempo de ejecución realiza las tareas de un programa.

Después de declarar int i; podemos decir que “i es un objeto de tipo int.” En POO / C++, “objeto” por lo general significa “instancia de una clase.” Por lo tanto, una clase define el comportamiento de, posiblemente, muchos objetos (instancias).

¿Cuándo una interface es “buena”?

Cuando se proporciona una visión simplificada de un fragmento de un software, y esté expresado en el vocabulario del usuario (donde “fragmento” es normalmente una clase o un grupo compacto de clases, y un “usuario” es otro desarrollador diferente del usuario final).

  • Se entiende por “Visión simplificada” a los detalles innecesarios que son ocultados intencionalmente. Esto reduce el la tasa de defectos ocasionados por el usuario.
  • “Vocabulario del usuario” significa que los usuarios no necesitan aprender un nuevo conjunto de palabras y conceptos. Esto reduce la curva de aprendizaje del usuario.

¿Qué es encapsulación?

Impedir el acceso no autorizado a alguna parte de información o funcionalidad.

La idea clave para ahorrar dinero es separar las partes volátiles de las partes estables. Encapsulación es como un fireball, que previene el acceso hacia las partes/segmentos volátiles, y permite sólo el acceso a las partes/segmentos estables. Esto evita el daño en nuestro software a través de operaciones en/con segmentos volatiles. En el contexto de software orientado a objetos, un “segmento/parte” es normalmente una clase o un grupo cerrado de las clases.

Las “partes volátiles” son los detalles de implementación. Si se trata de una sola clase, la parte volátil es normalmente encapsulado usando las palabras clave private y/o protected. Si se trata de un grupo cerrado de clases, encapsulación puede ser usada para negar el acceso a clases enteras de ese grupo. La herencia también puede ser utilizado como una forma de encapsulación.

Las “partes estables” son las interfaces. Una buena interfaz proporciona una visión simplificada en el vocabulario del usuario, y está diseñado desde el exterior (en este caso un “usuario”: otro desarrollador, no el usuario final que compra el software). Si se trata de una sola clase, la interfaz es simplemente la función publica o función amiga (friend). Si se trata de un grupo cerrado de las clases, la interfaz puede incluir a varias de las clases.

El diseño de una interfaz limpia y separando la interfaz de la implementación permite el uso de los usuarios. Pero al encapsular (poner en una “cápsula”) la aplicación obliga a los usuarios utilizar interfaces.

¿Cómo ayuda C++ en el equilibrio de seguridad vs usabilidad?

En C, la encapsulación se logra haciendo que las cosas sean estáticas en un módulo o unidad. Esto previene (o hace mas difícil) que otro módulo tenga acceso a éstas. (Por cierto, los datos estáticos de ámbito de archivo se consideran obsoleto en C++: no hagan eso.)

Desafortunadamente, este enfoque no es compatible con varias instancias de datos, ya que no hay soporte directo para crear varias instancias de módulo con datos estáticos. Si se necesitan varias instancias en C, los programadores suelen utilizar una estructura. Pero, por desgracia las estructuras C no admiten la encapsulación. Esto agrava el equilibrio entre la seguridad (ocultación de información) y la usabilidad (varias instancias).

En C + +, que puede tener varias instancias y encapsulación usando clases. La parte pública contiene la interfaz de la clase, que normalmente consiste en las funciones de miembro públicas y funciones amigas. Las partes privadas y/o protegidos de una clase contiene la implementación, que usualmente es donde los datos viven.

El resultado final es como una “estructura encapsulada”. Esto reduce el equilibrio entre seguridad (ocultación de información) y la usabilidad (múltiples instancias).

¿Cómo prevenir el acceso a miembros privados de mi clase?

No vale la pena el esfuerzo – encapsulación es para el código, no para personas.

Un programador no puede violar la encapsulación de las partes privadas y/o protegidas de la clase, siempre y cuando no se escriba el código que de alguna manera dependa de lo que ven. En otras palabras, la encapsulación no impide que otros programadores conozcan el interior de la clase, sino que previene que el código que escriben dependan de cosas en el interior de la clase. Su empresa no tiene que pagar un “costo de mantenimiento” para mantener la materia gris entre las orejas, pero sí tiene que pagar un costo de mantenimiento para mantener el código que sale de la punta de tus dedos. Lo que conocemos como una persona no aumenta el costo de mantenimiento, siempre que el código que se escribe depende de la interfaz en lugar de la implementación.

Además, esto es raramente un problema. Yo no conozco a ningún programador que intencionalmente trata de acceder a las partes privadas de una clase. “Mi recomendación en estos casos sería cambiar de programador, no el código” [James Kanze].

¿Puede un metodo acceder directamente a miembros no publicos de otras instancias de la misma clase?

Si.

El nombre this no es especial. El acceso es concedido o denegado en base a la referencia/puntero/objeto de la clase, no en base al nombre de la referencia/puntero/objeto. (Véase más abajo la letra pequeña.)

En efecto, C++ permite que sus métodos y funciones amigas a accedan a las partes no públicas de todos sus objetos, no sólo al objeto this, parece en un principio que debilita la encapsulación. Sin embargo es todo lo contrario: esta norma mantiene la encapsulación. He aquí por qué.

Sin esta regla, la mayoría de los miembros no públicos necesitarían un método get que sea público, ya que muchas clases tienen al menos un método o una función amiga que tiene un argumento explícito (es decir, un argumento que no es this) de su propia clase.

¿que? (usted se preguntará). Bueno, vamos a elaborar un ejemplo:

Considere la posibilidad de operador de asignación Algo::operator=(const Algo &x). Este operador de asignación, probablemente cambiará los miembros de datos del argumento de la izquierda, *this, sobre la base de los miembros de datos en el argumento de la derecha, x. Sin la regla C++ que se debate aquí, la única manera para que el operador de asignación acceda a los miembros no públicos de x sería que la clase Algo tuviera un método público get para cada dato no público. Eso sería un asco.

El operador de asignación no es el único que podría debilitar la encapsulación de no ser por esta regla. He aquí una lista parcial de los demás (!):

  • Constructor que recibe como parámetro una instancia de la misma clase (Constructor copia)
  • Los operadores de comparación: ==, =, <=, <,> =,>.
  • Los operadores binarios aritméticos: x + y, x – y , x * y, x / y, x % y.
  • Los operadores binarios: x ^ y, x + y, x | y.
  • Métodos estáticos que aceptan una instancia de la misma clase como parámetro.
  • Los métodos estáticos que crean/manipulan una instancia de la misma clase.
  • etc.

Conclusión: La encapsulación sería un desastre sin esta regla beneficiosa: la mayoría de los miembros no públicos de la mayoría de las clases terminarían teniendo método público get.

La letra pequeña: Hay otra regla que está relacionado con lo anterior: métodos y funciones amigas de una clase derivada pueden tener acceso a los miembros protegidos de cualquiera de sus propios objetos (los objetos de su clase o cualquier clase derivada de su clase), pero no para otros. Se ve un poco opaco, he aquí un ejemplo: supongamos que las clases D1 y D2 heredan directamente de la clase B, y clase B tiene un miembro protegido x. El compilador dará a los miembros y funciones amigas de D1 acceso directamente a los miembros de x de cualquier objeto que sabe que es un D1, como por ejemplo a través de un puntero D1*, una referencia &D1, un objeto D1, etc., sin embargo, el compilador dará un error al momento de compilar si un miembro D1 o una función amiga trata de acceder directamente a los miembros de x de cualquier cosa que no sea por lo menos un D1, como por ejemplo a través de un puntero B*, referencia &B, un objeto B, un puntero *D2, una referencia &D2, un objeto D2, etc., A modo de una analogía (imperfecta!!), a usted se le permite buscar en su propio bolsillo, pero no se le permite buscar en los bolsillos de tu padre, ni los bolsillos de tu hermano.

¿La Encapsulación es un dispositivo de Seguridad?

No.

Encapsulación != Seguridad.

Encapsulación previene errores, no el espionaje.

¿Cuál es la diferencia entre struct y class?

Los miembros de una estructura (struct) son públicos por defecto, mientras que en una clase son privados. Nota: usted debe especificar explicitamente el tipo de miembro de sus clases si son públicas, privadas o protegidas, en lugar de depender de los valores por defecto.

En otros casos struct y class son equivalentes.

Ok, basta de lenguaje técnico. Emocionalmente, la mayoría de los desarrolladores hacen una fuerte distinción entre una clase y una estructura. Una estructura simplemente se siente como una pila abierta de bits con muy poco en el camino de la encapsulación o la funcionalidad. Una clase se siente como un miembro vivo y responsable de la sociedad con servicios inteligentes, una barrera encapsulación fuerte, y una interfaz bien definida. Esa es la connotación que la mayoría de gente ya tiene, probablemente deba utilizar la palabra clave struct si usted tiene una clase que tiene muy pocos métodos y con datos públicos (tales cosas no existen en los sistemas bien diseñados!), pero de otra manera probablemente debería utilizar la palabra clave class.

Dejar una Respuesta