MQL5 El compilador no distingue entre una clase y un puntero a ella - página 13

 
Ilya Malev:

Simplemente no es lo mismo en MCL que en C++. Aquí un puntero no es un tipo de datos separado en absoluto, por lo que ambos son objetos, y una variable contiene su manejador.

¿verdad?

// тот же класс А что был выше

A a; // будет создан автообъект с реальным выделением памяти и инициализацией. 'a' является указателем со статусом POINTER_AUTOMATIC

A* pA; // память под объект не выделена, объекта нет. pA является указателем со статусом POINTER_INVALID или POINTER_DYNAMIC?

Pero no puedo decir con seguridad qué estado obtiene el puntero después de la creación:POINTER_INVALID o POINTER_DYNAMIC. En teoría, debería ser POINTER_INVALID hasta que obtenga la dirección de new.

 
Ilya Malev:

Es decir, declarar

Y tendrás exactamente el mismo "auto-objeto", pero tendrás que borrarlo tú mismo.

Eso no es auto...

 
SemenTalonov:

No puedo decir qué estado obtiene el puntero después de la creación,POINTER_INVALID o POINTER_DYNAMIC. En idea, debería ser POINTER_INVALIDO hasta que new obtenga la dirección.

Así es, el estado será POINTER_INVALID. Pero las variables a y pA son del mismo tipo. Sólo a es una constante, el constructor es llamado al crearse y el destructor al salir del bloque, mientras que pA es accedido aleatoriamente y el constructor y destructor son llamados arbitrariamente.

Una variable declarada sin * no puede obtener el estado POINTER_INVALID sin trucos especiales, esto también es cierto, pero no porque las variables sean de tipos diferentes, sino porque el compilador controla bien las llamadas al constructor y al destructor, y le prohíbe asignar otro valor.

Y como las variables son esencialmente del mismo tipo, no hay lógica para acceder a los métodos de la clase a través de ellas de forma diferente

 
Ilya Malev:

Esto es correcto, el estado será POINTER_INVALID. Pero las variables a y pA son del mismo tipo. Sólo a es constante y se llama al constructor en la creación, y al destructor en la salida del bloque, y pA es con acceso aleatorio, y con llamadas aleatorias al constructor y al destructor.

Así que todo el problema viene del hecho de que hay que distinguirlos. Es decir, un puntero requiere una actitud más responsable (laDINÁMICA).

 
SemenTalonov:

La única razón del problema es que hay que distinguirlos. Es decir, un puntero requiere una actitud más responsable (laDINÁMICA).

En mi opinión, el punto de la POO es sólo que las referencias a objetos pueden ser pasadas y almacenadas, y diferentes clases con diferentes comportamientos pueden ser escritas en ellas. Empezando a almacenar una referencia a un "auto-objeto" sin siquiera tocar el polimorfismo, ya se pierde toda su distinción (ya que no hay variables de usuario de tipo A&)

 
Ilya Malev:

Una variable declarada sin * no puede recibir el estado POINTER_INVALID sin trucos especiales, esto también es cierto, pero no porque las variables sean de tipos diferentes, sino porque el compilador controla bien las llamadas al constructor y al destructor, y prohíbe asignarle un valor diferente.

Exactamente. El puntero auto-objeto no cambiará su estado, mientras que el punteroPOINTER_DYNAMIC puede dejar de ser válido en cualquier momento de la ejecución del programa. Las razones no son tan importantes como la posibilidad de que se produzca ese acontecimiento.

 
SemenTalonov:

Exactamente. El puntero auto-objeto no cambiará su estado, mientras que el punteroPOINTER_DYNAMIC podría dejar de ser válido mientras el programa se está ejecutando. Las razones no son tan importantes como la posibilidad de que se produzca ese acontecimiento.

Hay una cura excelente y segura para esto:

class A{~A(){}};

void OnStart()
 {
  A*a=new A;
  delete a; // oops =))
 };
Bueno, creo que los programadores deben vigilar el tiempo de vida de los objetos y las formas de acceso a las variables y si la arquitectura está bien pensada desde el principio los errores se minimizarán un año o dos después de escribir el código...
 
De hecho, el tiempo de vida del objeto debe coincidir con el tiempo de vida de los punteros "vivos" a él. Por supuesto, el objeto dinámico POINTER_DYNAMIC es también una solución a medias, que crea problemas para una codificación poco meditada. Pero POINTER_AUTOMATIC tampoco me da esa escalabilidad que necesito para manejar los objetos adecuadamente. Necesitamos que sea así - si no se crearon punteros al objeto al salir del bloque, excepto la autovariable donde se creó, entonces borrar el objeto. Si las referencias se recibieron fuera del bloque actual, no elimine el objeto mientras estas referencias aún existan. Entonces habrá escalabilidad y el codificador no tendrá que vigilar el borrado por sí mismo todo el tiempo... (por ejemplo, si ahora escribes A* a = new A; y luego a = new A de nuevo, el primer objeto se perderá para siempre y está garantizado que habrá errores de fuga de memoria en los registros al salir del programa. ¿Y dónde mira el famoso optimizador de código?)
 
Ilya Malev:
Esencialmente, el tiempo de vida de un objeto debe corresponder al tiempo de vida de los punteros "vivos" a él. Por supuesto, el objeto dinámico de tipo mcl POINTER_DYNAMIC es también una solución medio opcional que causa problemas cuando la codificación no es muy eficiente. Pero POINTER_AUTOMATIC tampoco me da esa escalabilidad que necesito para manejar los objetos adecuadamente. Necesitamos que sea así - si no se crearon punteros al objeto al salir del bloque, excepto la autovariable donde se creó, entonces borrar el objeto. Si las referencias se recibieron fuera del bloque actual, no elimine el objeto mientras estas referencias sigan vivas. Entonces habrá escalabilidad y el codificador no tendrá que vigilar el borrado por sí mismo todo el tiempo... (por ejemplo, si ahora escribes A* a = new A; y luego a = new A de nuevo, el primer objeto se perderá para siempre y está garantizado que habrá errores de fuga de memoria en los registros al salir del programa. ¿Y dónde mira el famoso optimizador de código?)

Eso también fue una gran sorpresa. Al fin y al cabo, sabe exactamente cada byte que se ha escapado, pero no quiere liberarlo. ¿Así que no mira a los dino-punteros por el momento? Simplemente cuenta la diferencia entre la memoria solicitada y la liberada al final.

Y la vida útil y los punteros vacíos/perdidos son sólo uno de los problemas. Antes discutimos los problemas relacionados con la conversión implícita de punteros a objetos, ahí es donde se produce un choque) Cuando por error (por ejemplo, el compilador no debe haber permitido compilar dicho código), en lugar del puntero esperado se puede obtener una copia de un objeto por puntero, que a su vez puede apuntar a ninguna parte). Bueno, usted mismo ya ha escrito sobre las operaciones de comparación. Todo allí es también muy implícito.

 
SemenTalonov:

1. Eso también fue una gran sorpresa. Al fin y al cabo, sabe exactamente cada byte que se ha escapado y no quiere liberarlo. ¿Así que no mira a los dino-punteros por el momento? Sólo cuenta la diferencia entre la memoria solicitada y la memoria liberada al final.

Y la vida útil y los punteros vacíos/perdidos son sólo uno de los problemas.

1. En realidad no, escribir una simple GC a los objetos dinámicos sería pan comido para los desarrolladores, pero dejaron estos mensajes a propósito, para que los codificadores pudieran ver que tienen un fallo en su programa. Porque sus objetos dinámicos son tan semi-C# (no soy un experto en ello, pero por lo que he oído) - como si los objetos se comportan de la misma manera (no hay punteros, pero todos son objetos), y no se desarrolló ningún subsistema elaborado para ellos.

2. Bueno, sí, por supuesto, si las variables de los objetos fueran del mismo tipo (es decir: borradas no de forma automática o independiente, sino por el recolector de basura incorporado cuando no hay referencias a ellas), entonces todas las referencias a ellas serían exactamente iguales.

Razón de la queja: