Parece que C tiene sus propios cuasi-objetos, como 'estructuras' que pueden considerarse como objetos (en la forma de alto nivel que normalmente pensaríamos).
Y también, los archivos C son básicamente "módulos" separados, ¿verdad? ¿Entonces los módulos no son como "objetos"? Estoy confundido acerca de por qué C, que parece tan similar a C ++, se considera un lenguaje "procesal" de bajo nivel donde C ++ es "orientado a objetos" de alto nivel
* editar: (aclaración) ¿por qué y dónde, se dibuja la línea, para qué es y qué no es un 'objeto'?
object-oriented
procedural
Templario oscuro
fuente
fuente
Respuestas:
Juntos, usted y yo leemos la página de Wikipedia sobre programación orientada a objetos y marcamos las características de estructuras de estilo C que corresponden a lo que tradicionalmente se considera estilo orientado a objetos:
¿Las estructuras C consisten en campos y métodos junto con sus interacciones ? No.
¿Las estructuras C hacen alguna de estas cosas de manera "de primera clase"? No. El lenguaje funciona en tu contra en cada paso del camino.
¿Las estructuras C hacen esto? No.
¿Las estructuras C hacen esto? Si.
No.
¿Puede una estructura misma enviar y recibir mensajes? No. ¿Puede procesar datos? No.
¿Esto sucede en C? No.
¿Alguna de estas características de las estructuras C? No.
Precisamente, ¿qué características de las estructuras crees que están "orientadas a objetos"? Porque no puedo encontrar ninguna otra que el hecho de que estructuras definen tipos .
Ahora, por supuesto, puede crear estructuras que tengan campos que sean punteros a las funciones. Puede hacer que las estructuras tengan campos que sean punteros a matrices de punteros de función, correspondientes a tablas de métodos virtuales. Y así. Por supuesto, puede emular C ++ en C. Pero esa es una forma muy poco idiomática de programar en C; sería mejor usar C ++.
De nuevo, ¿qué características de los módulos piensas que los hacen actuar como objetos? ¿Los módulos admiten abstracción, encapsulación, mensajería, modularidad, polimorfismo y herencia?
La abstracción y la encapsulación son bastante débiles. Obviamente los módulos son modulares; Por eso se llaman módulos. ¿Mensajería? Solo en el sentido de que una llamada a método es un mensaje y los módulos pueden contener métodos. ¿Polimorfismo? No. ¿Herencia? No. Los módulos son candidatos bastante débiles para "objetos".
fuente
La palabra clave es "orientado", no "objeto". Incluso el código C ++ que usa objetos pero los usa como estructuras no está orientado a objetos .
C y C ++ pueden hacer OOP (aparte de no tener control de acceso en C), pero la sintaxis para hacerlo en C es inconveniente (por decir lo menos), mientras que la sintaxis en C ++ lo hace muy atractivo. C está orientado a procedimientos, mientras que C ++ está orientado a objetos, a pesar de capacidades centrales casi idénticas en ese sentido.
El código que usa objetos para implementar diseños que solo se pueden hacer con objetos (generalmente significa aprovechar el polimorfismo) es un código orientado a objetos. El código que usa objetos como poco más que bolsas de datos, incluso usando herencia en un lenguaje orientado a objetos, es realmente solo un código de procedimiento que es más complicado de lo que debe ser. El código en C que usa punteros de función que se cambian en tiempo de ejecución con estructuras llenas de datos está haciendo un poco de polimorfismo, y podría decirse que está "orientado a objetos", incluso en un lenguaje orientado a procedimientos.
fuente
Basado en los directores de más alto nivel:
Un objeto es una encapsulación de datos y comportamiento de una manera interconectada, de modo que operan como un todo que se puede instanciar varias veces y trabajar como una caja negra si conoce la interfaz externa.
Las estructuras contienen datos pero no comportamientos y, por lo tanto, no pueden considerarse objetos.
Los módulos contienen tanto el comportamiento como los datos, pero no están encapsulados de tal manera que los dos estén relacionados y ciertamente no se puedan instanciar varias veces.
Y eso es antes de llegar a la herencia y el polimorfismo ...
fuente
Las "estructuras" son solo datos. La prueba rápida y sucia habitual de "orientación a objetos" es: "¿Existe una estructura que permita encapsular el código y los datos como una sola unidad?". C falla eso, y por lo tanto es de procedimiento. C ++ pasa esa prueba.
fuente
this
puntero explícito , pero en ese caso, los datos y los medios para procesar los datos se encapsularán dentro de la estructura.#include
d en ella, y menos nada eliminado por no estar incluido condicionalmente (#if
,#ifdef
, y similares).C, al igual que C ++, tiene la capacidad de proporcionar abstracción de datos , que es un modismo del paradigma de programación orientado a objetos que existía antes.
OOP en C ++ extiende los medios para abstraer datos. Algunos dicen que es dañino , mientras que otros lo consideran una buena herramienta cuando se usa correctamente.
Sin embargo, encontrará que muchos "hackers" de C predican sobre cómo C es perfectamente capaz de obtener la cantidad justa de abstracción y cómo la sobrecarga creada por C ++ solo los distrae para resolver el problema real.
Otros tienden a mirarlo de una manera más equilibrada, aceptando tanto las ventajas como las desventajas.
fuente
Debes mirar el otro lado de la moneda: C ++.
En OOP, pensamos en un objeto abstracto (y diseñamos el programa en consecuencia), por ejemplo, un automóvil que puede detenerse, acelerar, girar a la izquierda o derecha, etc. Una estructura con un conjunto de funciones simplemente no se ajusta al concepto.
Con los objetos "reales" necesitamos ocultar los miembros, por ejemplo, o también podemos tener herencia con una relación "es" real y muchas más.
DESPUÉS DE LEER LOS COMENTARIOS A CONTINUACIÓN: Bueno, es cierto que (casi) todo se puede hacer con C (eso siempre es cierto), pero a primera vista pensé que lo que separa a c de c ++ es la forma en que piensas al diseñar un programa.
Lo único que realmente marca la diferencia es la imposición de políticas por parte del compilador . es decir, función virtual pura, y tal. pero esta respuesta solo se relacionará con problemas técnicos, pero creo que la diferencia principal (como se mencionó) es la forma original en que piensa mientras codifica, ya que C ++ le brinda una mejor sintaxis incorporada para hacer tales cosas, en lugar de hacer OOP en una manera algo torpe en C.
fuente
Lo dijiste tú mismo. Si bien C tiene cosas que son como objetos similares, todavía no son objetos, y es por eso que C no se considera un lenguaje OOP.
fuente
Orientado a objetos se refiere tanto a un patrón arquitectónico (o incluso a un metapatrón) como a los lenguajes que tienen características para ayudar a implementar o exigir el uso de este patrón.
Puede implementar un diseño "OO" (el escritorio Gnome es quizás el mejor ejemplo de OO hecho en C puro), ¡incluso he visto esto hecho con COBOL!
Sin embargo, poder implementar una dosis de diseño OO no hace que el lenguaje sea OO. Los puristas argumentarían que Java y C ++ no son realmente OO, ya que no puede anular o heredar los "tipos" básicos como "int" y "char", y Java no admite la herencia múltiple. Pero como son los lenguajes OO más utilizados y son compatibles con la mayoría de los paradigmas, la mayoría de los programadores "reales" a los que se les paga para producir código de trabajo los consideran los lenguajes OO.
C, por otro lado, solo admite estructuras (como lo hacen COBOL, Pascal y docenas de otros lenguajes de procedimiento), podría argumentar que admite la herencia múltiple, ya que puede usar cualquier función en cualquier dato, pero la mayoría consideraría esto como un error. que una característica
fuente
Veamos la definición de OO:
C no proporciona ninguno de esos tres. En particular, no proporciona mensajería , que es la más importante.
fuente
static
funciones internas ( ) y miembros de datos.Hay una serie de ingredientes clave para OO, pero los más importantes son que la mayoría del código no sabe qué hay dentro de un objeto (ven la interfaz de la superficie, no la implementación), que el estado de un objeto es una unidad administrada (es decir, cuando el objeto deja de ser, también lo hace su estado), y cuando algún código invoca una operación en un objeto, lo hacen sin saber exactamente qué es o implica esa operación (todo lo que hacen es seguir un patrón para lanzar un "Mensaje" sobre la pared).
C encapsula muy bien; el código que no ve la definición de una estructura no puede (legítimamente) mirar dentro de ella. Todo lo que necesita hacer es poner una definición como esta en un archivo de encabezado:
Por supuesto, habrá una necesidad de una función que construya
Foo
s (es decir, una fábrica) y que delegue parte del trabajo al propio objeto asignado (es decir, a través de un método de "constructor"), y que también tenga una manera de deshacerse del objeto nuevamente (mientras lo deja limpiar a través de su método de "destructor") pero eso es detalles.El envío de métodos (es decir, mensajes) también se puede hacer a través de la convención de que el primer elemento de la estructura es en realidad un puntero a una estructura llena de punteros de función, y que cada uno de esos punteros de función debe tomar un
Foo
como su primer argumento. Dispatch se convierte en una cuestión de buscar la función y llamarla con el argumento correcto reescribir, lo que no es tan difícil de hacer con una macro y un poco de astucia. (Esa tabla de funciones es el núcleo de lo que realmente es una clase en un lenguaje como C ++).Además, eso también le proporciona un enlace tardío: todo lo que sabe el código de envío es que está invocando un desplazamiento particular en una tabla señalada por el objeto. Eso solo debe establecerse durante la asignación e inicialización del objeto. Es posible utilizar esquemas de despacho aún más complejos que le brindan más dinamismo de tiempo de ejecución (a un costo de velocidad), pero son cerezas además del mecanismo básico.
Sin embargo, eso no significa que C sea un lenguaje OO. La clave es que C te deja hacer todo el trabajo complicado de escribir las convenciones y el mecanismo de envío tú mismo (o usar una biblioteca de terceros). Eso es mucho trabajo. Tampoco proporciona soporte sintáctico o semántico, por lo que implementar un sistema de clase completo (con cosas como la herencia) sería innecesariamente doloroso; Si se trata de un problema complejo que está bien descrito por un modelo OO, un lenguaje OO será muy útil para escribir la solución. La complejidad adicional puede justificarse.
fuente
Creo que C está perfectamente bien y decente para implementar conceptos orientados a objetos, encogerse de hombros . La mayoría de las diferencias, según lo veo, entre el subconjunto común de denominadores de lenguajes considerados orientados a objetos son menores y de naturaleza sintáctica desde mi punto de vista pragmático.
Comencemos con, digamos, ocultar información. En C podemos lograr eso simplemente ocultando la definición de una estructura y trabajando con ella a través de punteros opacos. Eso modela efectivamente la distinción
public
vs.private
los campos de datos a medida que lo hacemos con las clases. Y es bastante fácil de hacer y apenas anti-idiomático, ya que la biblioteca C estándar se basa en gran medida en esto para lograr ocultar la información.Por supuesto, pierde la capacidad de controlar fácilmente exactamente dónde se asigna la estructura en la memoria mediante el uso de tipos opacos, pero eso es solo una diferencia notable entre, por ejemplo, C y C ++. C ++ es definitivamente una herramienta superior cuando se compara su capacidad para programar conceptos orientados a objetos sobre C mientras se mantiene el control sobre los diseños de memoria, pero eso no significa necesariamente que Java o C # sean superiores a C en ese sentido, ya que esos dos te hacen pierde por completo la capacidad de controlar dónde se asignan los objetos en la memoria.
Y tenemos que usar una sintaxis como
fopen(file, ...); fclose(file);
en lugar de unfile.open(...); file.close();
gran whoop. ¿A quien le importa? Tal vez solo alguien que se apoya mucho en la autocompletación en su IDE. Admito que puede ser una característica muy útil desde un punto de vista práctico, pero tal vez no una que requiera una discusión sobre si un lenguaje es adecuado para la POO.Carecemos de la capacidad de implementar efectivamente los
protected
campos. Me someteré totalmente allí. Pero no creo que haya una regla concreta que diga: " Todos los lenguajes OO deberían tener una función que permita a las subclases acceder a miembros de una clase base a la que los clientes normales aún no deberían tener acceso ". Además, rara vez veo casos de uso para miembros protegidos que no son al menos un poco sospechosos de convertirse en un obstáculo de mantenimiento.Y, por supuesto, tenemos que "emular" el polimorfismo OO con tablas de punteros de función y punteros para enviarlos dinámicamente con un poco más de repetitivo para inicializar aquellos analógicos
vtables
yvptrs
, pero un poco de repetitivo nunca me causó mucho dolor.La herencia es muy parecida. Podemos modelarlo fácilmente a través de la composición, y en el funcionamiento interno de los compiladores se reduce a lo mismo. Por supuesto, perdemos la seguridad de los tipos si queremos hacer downcast , y diría que si quieres hacer downcasts , no uses C para eso porque las cosas que las personas hacen en C para emular el downcast pueden ser horribles por un tipo punto de vista de seguridad, pero prefiero que la gente no se desanime en absoluto. La seguridad de tipos es algo que puede comenzar a pasar por alto fácilmente en C, ya que el compilador proporciona tanta libertad para interpretar cosas como solo bits y bytes, sacrificando la capacidad de detectar posibles errores en tiempo de compilación, pero algunos lenguajes considerados no están orientados a objetos ni siquiera estáticamente escrito.
No sé, creo que está bien. Por supuesto, no usaría C para intentar crear una base de código a gran escala que se ajuste a los principios SÓLIDOS, pero no necesariamente se debe a sus defectos en el frente orientado a objetos. Muchas de las características que extrañaría si intentara usar C para tal fin estarían relacionadas con las características del lenguaje que no se consideran directamente un requisito previo para la POO, como la seguridad de tipo fuerte, destructores que se invocan automáticamente cuando los objetos están fuera de alcance, operador sobrecarga, plantillas / genéricos y manejo de excepciones. Es cuando me faltan esas características auxiliares que alcanzo para C ++.
fuente
foo_create
yfoo_destroy
, yfoo_clone
, por ejemplostruct Foo
en tal caso es asegurarnos de que sus miembros de datos no puedan ser accedidos y mutados por el mundo exterior, que los tipos opacos (declarados pero no definidos para el mundo exterior) dan de inmediato. Podría decirse que es una forma más fuerte de ocultar información, a la par de la de unpimpl
en C ++.No es una mala pregunta
Si va a llamar a c un lenguaje OO, necesitaría llamar a casi todos los lenguajes de procedimiento OO también. Entonces haría que el término no tuviera sentido. c no tiene soporte de idioma para OO. Si tiene estructuras, pero las estructuras
types
no son clases.En realidad, c no tiene muchas características en comparación con la mayoría de los lenguajes. Se utiliza principalmente por su velocidad, simplicidad, popularidad y soporte, incluidas toneladas de bibliotecas.
fuente