Pregunta

Disculpas si esto se ha preguntado antes, no estoy muy seguro de la terminología ni de cómo hacer la pregunta.

Me pregunto si existen bibliotecas o mejores prácticas para implementar modelos de objetos en C++.Si tengo un conjunto de clases donde las instancias de estas clases pueden tener relaciones entre sí y se puede acceder a ellas entre sí a través de varios métodos, quiero elegir un buen conjunto de estructuras de datos subyacentes para administrar estas instancias y sus interrelaciones.Esto es fácil en Java ya que maneja la asignación de memoria y la recolección de basura por mí, pero en C++ tengo que hacerlo yo mismo.

HTML Modelo de objetos de documento (DOM) es un ejemplo;Como otro ejemplo (artificial), supongamos que tengo estas clases:

  • Entity
  • Person (subclase de Entity)
  • Couple (subclase de Entity)
  • Property
  • House (subclase de Property)
  • Pet (subclase de Property)
  • Car (subclase de Property)

y estas relaciones:

  • Entity
    • tiene 1 home de clase House
    • tiene 0 o más pets de clase Pet
    • tiene 0 o más cars de clase Car
    • tiene 0 o más children de clase Person
  • Person
    • tiene 0 o 1 spouse de clase Person
    • tiene 0 o 1 marriage de clase Couple
    • tiene 0 o 1 parents de clase Entity (¡en este modelo los padres no existen si no están vivos!)
  • Couple
    • tiene 2 members de clase Person
  • Property
    • tiene 1 owner de clase Entity

Ahora que he pensado en estos objetos y sus relaciones, quiero empezar a crear estructuras de datos, métodos y campos para manejarlos, y aquí es donde me pierdo, ya que tengo que ocuparme de la asignación de memoria y la gestión de la vida útil y todo eso. .Puede encontrarse con problemas como los siguientes:Podría querer poner un objeto en un std::map o un std::vector, pero si hago eso, no puedo almacenar punteros a esos objetos ya que pueden reubicarse cuando el mapa o el vector crece o se reduce.

Un enfoque que utilicé cuando trabajaba mucho con COM es tener una colección oculta que contuviera todo.Cada objeto de la colección tenía un ID único (ya sea un número o un nombre) mediante el cual se podía buscar en la colección, y cada objeto tenía un puntero a la colección.De esa manera, si tiene un objeto que quiere apuntar a otro objeto, en lugar de literalmente mantener un puntero a otro objeto, almaceno el ID y puedo buscarlo a través de la colección oculta.Puedo usar el recuento de referencias para abordar automáticamente los problemas de duración (excepto en el caso de ciclos disjuntos, a veces eso no es un problema).

¿Existen otros enfoques?¿O existen bibliotecas para facilitar este tipo de cosas en C++?

editar: entonces tienes otros problemas, como que es probable que las relaciones entre objetos sean mutables en muchos casos, y tienes que pensar con anticipación sobre cómo se deben almacenar las referencias a los objetos y qué métodos se deben proporcionar para acceder a los objetos entre sí.Por ejemplo, si tengo un identificador para un Person X, y quiero representar el concepto de "encontrar al hijo de X llamado George", entonces tengo que almacenar el nombre "George" en lugar de un número de hijo:los hijos pueden almacenarse en un vector, y es posible que pueda llamar a X.getChildCount() y X.getChild(0), pero es posible que "George" no siempre sea el hijo número 0, ya que se pueden insertar otros hijos antes de "George". " en el vector hijo.O X puede tener dos, tres o cuatro hijos más también llamados "George".O "George" puede cambiar su nombre a "Anthony" o "Georgina".En todos estos casos, probablemente sea mejor utilizar algún tipo de identificación única e inmutable.

edición 2: (y limpiaré un poco mi pregunta una vez que aclare esto) Puedo ocuparme de la elección de métodos y nombres de propiedades, puedo decidir si debo usar un mapa, una lista o un vector.Eso es bastante sencillo.Los problemas que estoy tratando de abordar específicamente son:

  • cómo hacer que un objeto almacene una referencia a otro objeto, cuando esos objetos pueden ser parte de estructuras de datos que se reasignan
  • cómo lidiar con la gestión de la vida útil de los objetos, cuando existen relaciones recíprocas entre objetos
¿Fue útil?

Solución

Escribiste sobre el almacenamiento de objetos de tu modelo de objetos dentro de std::vector, etc.y problemas con el uso de punteros hacia ellos.Eso me recuerda que es bueno dividir las clases de C++ en dos categorías (no estoy seguro de la terminología aquí):

  1. Clases de entidad que representan objetos que forman parte de su modelo de objetos.Generalmente son polimórficos o potencialmente lo serán en el futuro.Se crean en el montón y siempre se hace referencia a ellos mediante punteros o punteros inteligentes.Nunca los crea directamente en la pila, como miembros de clase/estructura, ni los coloca directamente en contenedores como std::vectors.No tienen constructor de copia ni operador = (puedes hacer una nueva copia con algún método Clonar).Puedes compararlos (sus estados) si es significativo para ti, pero no son intercambiables porque tienen identidad.Cada dos objetos son distintos.

  2. Clases de valor que implementan tipos primitivos definidos por el usuario (como cadenas, números complejos, números grandes, punteros inteligentes, envoltorios de identificadores, etc.).Se crean directamente en la pila o como miembros de clase/estructura.Se pueden copiar con el constructor de copia y el operador =.No son polimórficos (polimorfismo y operador= no funcionan bien juntos).A menudo colocas sus copias dentro de contenedores stl.Rara vez se almacenan indicaciones sobre ellos en ubicaciones independientes.Son intercambiables.Cuando dos instancias tienen el mismo valor, puedes tratarlas como iguales.(Sin embargo, las variables que los contienen son cosas diferentes).

Hay muchas muy buenas razones para romper las reglas anteriores.Pero observé que ignorarlos desde el principio conduce a programas ilegibles, poco confiables (especialmente cuando se trata de administración de memoria) y difíciles de mantener.


Ahora volvamos a tu pregunta.

Si desea almacenar un modelo de datos con relaciones complejas y una forma sencilla de realizar consultas como "buscar al hijo de X llamado George", ¿por qué no considerar alguna base de datos relacional en memoria?

Tenga en cuenta que cuando vaya a implementar de manera eficiente a) relaciones bidireccionales más complejas y b) consultas basadas en diferentes propiedades de objetos, probablemente necesitará crear estructuras de datos indexados que sean muy similares a lo que hace la base de datos relacional en su interior.¿Sus implementaciones (ya que habrá muchas, en un solo proyecto) realmente serán más efectivas y sólidas?

Lo mismo ocurre con las "colecciones de todo" y los identificadores de objetos.De todos modos, deberá realizar un seguimiento de las relaciones entre objetos para evitar identificaciones sin objetos.¿En qué se diferencia de los punteros?Aparte de obtener errores significativos en lugar de volverse loco por toda la memoria, eso es ;-)


Algunas ideas para la gestión de la memoria:

  • Propiedad fuerte:cuando puede declarar que una entidad vive solo mientras su propietario y no hay posibilidad de que existan punteros independientes hacia ella, puede simplemente eliminarla en el destructor del propietario (o con Scoped_ptr).

  • Alguien ya propuso smart_ptr.Son geniales y se pueden usar con contenedores stl.Sin embargo, están basados ​​en contadores de referencia, por lo que no cree ciclos :-(.No conozco ningún puntero automático de C++ ampliamente utilizado que pueda manejar ciclos.

  • Tal vez haya algún objeto de nivel superior que posea todos los demás objetos.P.ej.A menudo se puede decir que todas las piezas pertenecen a un documento, un algoritmo o una transacción.Se pueden crear en el contexto del objeto de nivel superior y luego eliminarse automáticamente cuando se elimina su objeto de nivel superior (cuando elimina el documento de la memoria o finaliza la ejecución del algoritmo).Por supuesto, no puedes compartir piezas entre objetos de nivel superior.

Otros consejos

Hay alrededor de un millón (en una estimación conservadora) de esto. Realmente estás preguntando & Quot; ¿cómo diseño el software en C ++ & Quot ;. Y la respuesta es, me temo, & "; ¿Qué va a hacer su software? &"; - simplemente saber que quieres tratar con personas y casas no es suficiente.

¿No es este el objetivo de OOP? Que lo que está preguntando es un detalle de implementación, que se esconde detrás de la interfaz pública de estas clases y, por lo tanto, no tiene que preocuparse, porque puede cambiarlo sin cambiar la interfaz. Así que adelante, pruébalo de la manera que sugieres. Luego, si hay un problema de rendimiento, memoria u otro, puede solucionar la implementación sin romper el resto del código.

Me parece que almacenar sus datos en una base de datos, y usar algún tipo de mapeo relacional de objetos, podría ser otra opción a considerar.

Podría usar boost :: shared_ptr para solucionar los problemas de memoria. Luego puede copiar libremente shared_ptr, devolverlo de las funciones, usarlo como una variable local, etc.

A Person podría tener un std::map< string, boost::shared_ptr<Person> >, por lo que X.getChild("George") simplemente buscaría al niño en el mapa y devolvería el puntero. Creo que entiendes el concepto, así que te dejo el resto como ejercicio;)

Jason, mi fuente favorita es el Libro de preguntas frecuentes de C ++ . El problema es que efectivamente está preguntando & "; ¿Cómo puedo usar C ++ para la programación orientada a objetos? &";

Lo mejor que puedo decir en una respuesta SO es esta:

todas estas cosas serán clases en C ++, y las relaciones, etc. se parecerán mucho a los lenguajes recolectados de basura a los que estás acostumbrado: si necesitas una relación entre una Persona y su Hijo llamada " george " ;, selecciona una estructura de datos que puede almacenar personas o niños indexados por nombre.

La administración de memoria es en realidad más fácil que en C, si sigue algunas reglas: asegúrese de que todos los objetos que los necesitan tengan destructores, asegúrese de que los destructores limpien todo lo que posee el objeto y luego asegúrese de que siempre los ponga Objetos construidos dinámicamente en contextos donde quedan fuera de alcance cuando ya no son necesarios. Esos no cubrirán todos los casos, pero le ahorrarán probablemente el 80 por ciento de los errores de asignación de memoria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top