2009-02-19 24 views
8

Estoy aprendiendo C++ (y la programación en general) y estoy tratando de hacer una clase Point y una clase Line.Clase de punto y línea en C++?

Una línea debe estar compuesta por objetos de 2 puntos.

¿Pueden los gurús de C++ revisar mi trabajo y decirme si es así como debería usar punteros, referencias y clases?

class Point 
{ 
    private: 
     int x, y; 
    public: 
     Point() : x(0), y(0) {} 
     Point(int x, int y) : x(x), y(y) {} 
} 

class Line 
{ 
    private: 
     Point *p1; 
     Point *p2; 
    public: 
     Line(Point &p1, Point &p2) : p1(p1), p2(p2) {} 

     void setPoints(Point &p1, Point &p2) 
     { 
      this->p1 = p1; 
      this->p2 = p2; 
     } 
} 
+0

Su ejemplo no debe compilarse, porque está mezclando punteros ("Point * p1") con referencias ("Point & p1"). – ChrisW

Respuesta

7

No debe utilizar punteros en absoluto en su código. Usa objetos reales Los punteros se usan muy raramente en C++.

class Point 
{ 
    private: 
     int x, y; 
    public: 
     Point() : x(0), y(0) {} 
     Point(int x, int y) : x(x), y(y) {} 
} 

class Line 
{ 
    private: 
     Point p1; 
     Point p2; 
    public: 
     Line(const Point & p1, const Point & p2) : p1(p1), p2(p2) {} 

     void setPoints(const Point & ap1, const Point & ap2) 
     { 
      p1 = ap1; 
      p2 = ap2; 
     } 
} 
+0

Los punteros no se usan raramente en C++. Obviamente no has hecho ninguna programación de GUI, donde los árboles están involucrados. =] – strager

+0

doh, supongo que en más de 20 años de programación en C++ nunca encontré un árbol - tonto. –

+0

No necesito usar punteros para construir un árbol. –

2

yo preferiría este ...

class Point 
{ 
    private: 
     int x, y; 
    public: 
     Point() : x(0), y(0) {} 
     Point(int x, int y) : x(x), y(y) {} 
} 

class Line 
{ 
    private: 
     Point p1; 
     Point p2; 
    public: 
     Line(const Point &p1, const Point &p2) : p1(p1), p2(p2) {} 

     void setPoints(const Point &p1, const Point &p2) 
     { 
      this->p1 = p1; 
      this->p2 = p2; 
     } 
} 
+0

p1 = p1; Seguramente algún error aquí? –

+0

Sí, tienes razón. Ahora arreglado. :) –

2

No hay necesidad de utilizar punteros en su clase de la línea.

Además, la línea siguiente es incorrecto:

Line(Point &p1, Point &p2) : p1(p1), p2(p2) {} 

¿Por qué? Está asignando un Point & (p1) a un Point * (Line :: p1), que es ilegal. Querrá punteros allí.

Su clase de punto no tiene forma de modificar sus datos. No muy útil ...

Una clase Línea para mí sería algo como esto:

class Line 
{ 
    private: 
     Point p1, p2; 

    public: 
     Line() 
     { 
     } 

     Line(Point start, Point end) : 
      p1(start), p2(end) 
     { 
     } 

     const Point &startPoint() const 
     { 
      return p1; 
     } 

     Point &startPoint() 
     { 
      return p1; 
     } 

     const Point &endPoint() const 
     { 
      return p2; 
     } 

     Point &endPoint() 
     { 
      return p2; 
     } 
}; 

Ahora puede editar su línea como la siguiente:

Line line; 
line.startPoint() = Point(4, 2); 
line.endPoint() = Point(6, 9); 
+0

usted no es un creyente en el uso de const, entonces? –

+0

lo siento, ignore el último - leyó mal el código –

+0

¿Por qué tiene 2 métodos de obtención para cada punto, uno con const y uno sin? –

1

Un par de cosas que noté :

  • Puede combinar sus dos constructores de puntos en un solo constructor con valores predeterminados.
  • Su uso de punteros es bastante innecesario. Usa el objeto mismo.
  • Está utilizando tanto punteros como referencias indistintamente. No los mezcles, o ve el último punto.
+0

Un solo constructor con valores predeterminados también permitiría que el Punto (1) construya un punto (con el mismo efecto que el Punto (1,0)). Yo prefiero los dos constructores. –

1

Veo poco valor en la fabricación de los punteros de Point (que no sean por valor de ironía). Su punto toma 8 bytes en un sistema de 32 bits (2 int). Un puntero toma 4 bytes. estás guardando 4 bytes.

En cuanto a la corrección, su constructor de líneas toma referencias, pero las está asignando a punteros. Eso ni siquiera debería compilar. También estás haciendo lo mismo en setPoints. Sería mejor simplemente hacer los dos puntos objetos reales y copiar sus valores.

+0

¿Eh? ¿Cómo ahorra el puntero 4 bytes? Señalará los 8 bytes de una instancia de punto, lo que significa que * agrega * 4 bytes. Esto es en mi humilde opinión un poco engañoso. – mghie

+0

Me refería a la clase en sí. Supongamos que tiene 10 clases de línea que usan la misma instancia de punto. Es un ahorro mínimo. –

+0

Pero eso es aún peor, ¿cómo sabría cuándo liberar un Punto * si es referenciado por más de una Línea? Todo esto tratando de tratar a C++ como un lenguaje de GC no conduce a ninguna parte. Solo puedo estar de acuerdo con zabzonk: en el moderno C++ raras veces hay una razón para usar punteros crudos. – mghie

0
class Line 
{ 
    private: 
     Point *p1; /* No memory allocated */ 
     Point *p2; /* No memory allocated */ 
    public: 
     Line(Point &p1, Point &p2) : p1(p1), p2(p2) {} 

     void setPoints(Point &p1, Point &p2) /* passed references to Point objects */ 
     { 
      this->p1 = p1; /* asssiging Point objects to Point *! */ 
      this->p2 = p2; /* asssiging Point objects to Point *! */ 
     } 
} 

La función setPoints() no funcionaría - a primera vista. Debe ser

  void setPoints(Point &p1, Point &p2) 
      { 
       this->p1 = &p1; /* asssiging Point objects to Point *! */ 
       this->p2 = &p2; /* asssiging Point objects to Point *! */ 
      } 

Por otra parte, no tenemos control sobre cuándo p1 y p2 son destruidos.Es mejor crear esto-> p1 y esto-> p2 usando los datos en p1 y p2 para que el destructor tenga control sobre la memoria

5

+1 lo que dijo zabzonk.

La hora de usar punteros, a medida que los haya utilizado, sería:

  1. Usted tiene varios puntos
  2. Desea crear líneas usando esos puntos
  3. Usted desea cambiar los valores de los puntos y tienen las líneas modificadas implícitamente.

El paso 3 anterior se puede lograr si las líneas contienen punteros a los puntos existentes. Sin embargo, introduce complejidad (por ejemplo, "cuando destruyes una instancia de Point, ¿qué ocurre con instancias de línea que contienen punteros al punto?"), Que no existe cuando (como sugirió zabzonk) cada línea contiene sus propios valores de punto.

5

Independientemente de si los miembros Point de su clase de línea son punteros o no, crean un tipo de clase muy diferente. El uso de punteros dará como resultado un enfoque clásico de estilo CoGo, que puede considerarse como puntos que son como clavos en un tablero, y las líneas son bandas elásticas que conectan esas uñas. Cambiar un punto es como mover un clavo, todos los trabajos de línea asociados siguen automáticamente, lo cual es deseable en ciertas aplicaciones.

El uso de puntos literales significa que las líneas son todas independientes una de otra, lo que es apropiado para otros tipos de aplicaciones.

Esta es una decisión crítica de diseño para sus clases en esta etapa.

Editar: Como se señaló en otras publicaciones y el comentario a continuación, la utilización de punteros simples para lograr la asociación entre varias líneas y puntos también presenta un problema potencial grave. Específicamente, si un punto se borra o mueve en la memoria, todos los punteros que se refieren a ese punto deben actualizarse. En la práctica, esto tiende a superarse mediante el uso de ID de puntos únicos para buscar un punto en lugar de simples punteros. Esto también permite que las estructuras de CoGo se serialicen/guarden fácilmente. Por lo tanto, nuestra clase de punto tendría un miembro estático para obtener un punto basado en ID, y nuestra clase de línea tendría dos ID de punto en lugar de punteros.

+0

He upvoted su respuesta el día de hoy, pero ahora no estoy tan seguro de los otros: hay mucha confusión en otras respuestas sobre cuándo usar punteros, y aunque estoy de acuerdo con su premisa de que dejar de lado los problemas de gestión de toda la vida usando el punto * completamente. ¿Tal vez podrías editar tu excelente respuesta? – mghie

0

El compilador dará errores en el código:

  1. En la lista de inicialización del constructor de la línea, está asignando P1 Punto de referencia a p1 miembro de la clase, que es un puntero a punto. Para que esto se compile, debe usar Línea (Punto & p1, Punto & p2): p1 (& p1), p2 (& p2).
  2. El mismo problema se produce en el método de puntos de ajuste. Shoudl que ser cambiado a esto:> = p1 p1 &, etc.
  3. tema menor: tras el cierre de la clase}, puesto en un punto y coma (;)

Creo que con esto concluye la sintaxis.

Hay otro problema con el código:

Los miembros de la clase P1 y P2 de la línea son punteros a Point casos. ¿Quién crea estas instancias y quién las eliminará cuando ya no las necesiten?Si diseña su clase tal como es ahora, el código que crea una instancia de línea pasa dos instancias de punto al constructor. Si estas instancias de punto se eliminan antes de que la línea sea, la línea queda con dos punteros colgantes (punteros que ya no hacen referencia a una instancia válida).

Además, las instancias de dos puntos que ahora son parte de la línea se pueden modificar desde el código fuera de la clase de línea. Esto puede conducir a situaciones muy indeseables.

En esta situación, declararía los miembros p1 y p2 como Punto (en lugar de un puntero a Punto). De esta forma, las instancias de Point que se pasan al constructor se copian a los miembros de la clase. Mientras exista la Línea, los miembros del Punto existirán. Solo pueden ser cambiados por la clase de línea en sí.

0

Antes de preguntar sobre la opinión del gurú del lenguaje, comience a pensar sobre el diseño, en este caso especialmente sobre la vida útil de sus objetos: ¿necesita un punto existir sin una línea? ¿Las líneas comparten puntos? ¿Quién crea un punto? ¿Cuándo deja de existir? Son dos puntos con las mismas coordenadas idénticas, o simplemente iguales (una podría ser roja, la otra podría ser azul)? ...

Parece que la mayoría acuerda aquí que debe usar la semántica de valores en una base de código tan pequeña. Algunas veces, el problema requiere que el mismo objeto (es decir, Punto) sea referenciado por muchos lados, luego use punteros (o referencias).

La elección entre un puntero y una referencia es otra cosa. Prefiero usar una referencia cuando implemente la agregación (una línea no puede existir sin sus puntos finales) y un puntero cuando implemente una relación menos "íntima".

+0

Mi descripción de la diferencia entre una referencia y un puntero está en http://stackoverflow.com/questions/448056/c-singleton-getinstance-return/448068#448068 – ChrisW

+0

tx. Mucha discusión sobre ese tema, también. – xtofl

0

Utilice objetos de punto en su clase de línea. La propiedad de los puntos no está clara y te arriesgas a terminar con punteros colgantes o memoria filtrada.

Su constructor predeterminado es un problema ya que rara vez querrá crear un punto en (0,0). Es mejor configurar los valores x, y predeterminados en algo como MAXINT y afirmar que cualquier uso de Point no tiene MAXINT como uno de sus valores. Tener una función is_valid() para que los clientes puedan probar. Su clase de línea también puede tener una condición previa de que sus dos puntos no son inválidos. La recompensa de no permitir que un punto válido tenga un valor MAXINT es que usted puede detectar cuándo los Puntos no se han inicializado correctamente, y eliminará algunos errores difíciles de encontrar en el uso de la clase.

Cuestiones relacionadas