2009-02-27 40 views
6

He estado desarrollando desde hace un tiempo, y no he usado punteros en mi desarrollo hasta el momento.Usando punteros en Delphi

¿Cuáles son los beneficios de los punteros? ¿Se ejecuta una aplicación más rápido o usa menos recursos?

Porque estoy seguro de que los indicadores son importantes, ¿me puede "señalar" algunos artículos, básicos pero buenos para empezar a usar punteros en Delphi? Google me da demasiados, resultados demasiado especiales.

Respuesta

30

Un puntero es una variable que apunta a una parte de la memoria. Las ventajas son:

  • puede dar esa pieza de memoria del tamaño que desee.
  • solo tiene que cambiar un puntero para apuntar a un trozo de memoria diferente que ahorra mucho tiempo de copiado.

Delphi utiliza una gran cantidad de punteros ocultos. Por ejemplo, si está usando:

var 
    myClass : TMyClass; 
begin 
    myClass := TMyClass.Create; 

myClass es un puntero al objeto.

Otro ejemplo es la matriz dinámica. Esto también es un puntero.

Para comprender más sobre los punteros, debe comprender más acerca de la memoria. Cada pieza de datos puede existir en diferentes piezas de datos.

Por ejemplo variables globales:

unit X; 

interface 

var 
    MyVar: Integer; 

Una variable global se define en el datasegment. El segmento de datos es fijo. Y durante la vida del programa, estas variables están disponibles. Lo que significa que la memoria no puede usarse para otros usos.

las variables locales:

procedure Test; 
var 
    MyVar: Integer; 

Existe una variable local en la pila. Esta es una pieza de memoria que se usa para la limpieza. Contiene los parámetros para la función (ok algunos se ponen en un registro pero eso no es importante ahora). Contiene la dirección de retorno para que la CPU sepa dónde volver si el programa ha finalizado. Y contiene las variables locales usadas en las funciones. Las variables locales solo existen durante el tiempo de vida de una función. Si la función finaliza, no puede acceder a la variable local de manera confiable.

variables de Heap:

procedure Test2; 
var 
    MyClass: TMyClass; 
begin 
    MyClass := TMyClass.Create; 

El MyClass variable es un puntero (que es una variable local que se define en la pila). Al construir un objeto, asigna una pieza de memoria en el montón (la gran parte de la "otra" memoria que no se usa para programas y pilas). La variable MyClass contiene la dirección de esta pieza de memoria. Las variables de montón existen hasta que las libera. Eso significa que si sale de la función Test2 sin liberar el objeto, el objeto aún existe en el montón. Pero no podrá acceder a él porque la dirección (variable MyClass) ha desaparecido.

mejores prácticas

Está casi siempre preferencia para asignar y desasignar una variable puntero en el mismo nivel.

Por ejemplo:

var 
    myClass: TMyClass; 
begin 
    myClass := TMyClass.Create; 
    try 
    DoSomething(myClass); 
    DoSomeOtherthing(myClass); 
    finally 
    myClass.Free; 
    end; 
end; 

Si puede, trate de evitar las funciones que devuelven una instancia de un objeto. Nunca es seguro si la persona que llama necesita deshacerse del objeto. Y esto crea fugas de memoria o bloqueos.

+0

Espero que esto es suficiente información, puedo agregar más si lo desea. –

+0

+1 Excelente explicación. ¡Buen trabajo! –

+0

Gracias, me gusta ayudar. Pero siempre es bueno si se aprecia. –

2

Probablemente haya utilizado punteros, pero simplemente no lo sabe. Una variable de clase es un puntero, una cadena es un puntero, una matriz dinámica es un puntero, Delphi simplemente lo oculta por usted. Los verás cuando estés realizando llamadas a la API (lanzando cadenas a PChar), pero incluso entonces Delphi puede esconderse mucho.

Ver la respuesta de Gamecats para las ventajas de los punteros.

En este About.com article puede encontrar una explicación básica de los punteros en Delphi.

2

Los apuntadores son necesarios para algunas estructuras de datos. El ejemplo más simple es una lista vinculada. La ventaja de tales estructuras es que puedes recombinar elementos sin moverlos en la memoria. Por ejemplo, puede tener una lista vinculada de grandes objetos complejos e intercambiar dos de ellos muy rápidamente porque realmente tiene que ajustar dos punteros en lugar de mover estos objetos.

Esto se aplica a muchos idiomas, incluido Object Pascal (Delphi).

10

Se le ha dado un montón de buenas respuestas hasta el momento, pero a partir de la respuesta que ya se está ocupando de los punteros cuando se utiliza cadenas largas, matrices dinámicas y referencias a objetos usted debe comenzar a preguntarse por qué haría uso punteros, en lugar de cadenas largas, matrices dinámicas y referencias a objetos. ¿Hay alguna razón para seguir usando punteros, dado que Delphi hace un buen trabajo ocultándoselos, en muchos casos?

Permítame darle dos ejemplos del uso del puntero en Delphi. Verá que probablemente esto no sea relevante para usted si escribe principalmente aplicaciones comerciales. Sin embargo, puede volverse importante si alguna vez necesita usar Windows o funciones de API de terceros que no son importadas por ninguna de las unidades Delphi estándar, y para las cuales no se pueden encontrar unidades de importación en (por ejemplo) las bibliotecas JEDI. Y puede ser la clave para lograr ese último bit de velocidad necesario en el código de procesamiento de cadenas.

punteros se pueden utilizar para hacer frente a los tipos de datos de diferentes tamaños (desconocidos en tiempo de compilación)

considerar el tipo de datos de mapa de bits de Windows. Cada imagen puede tener diferentes anchos y alturas, y hay diferentes formatos que varían desde blanco y negro (1 bit por píxel) sobre 2^4, 2^8, 2^16, 2^24 o incluso 2^32 valores de gris o colores . Eso significa que no se sabe en tiempo de compilación cuánta memoria ocupará un mapa de bits.

En windows.pas existe la TBitmapInfo tipo:

type 
    PBitmapInfo = ^TBitmapInfo; 
    tagBITMAPINFO = packed record 
    bmiHeader: TBitmapInfoHeader; 
    bmiColors: array[0..0] of TRGBQuad; 
    end; 
    TBitmapInfo = tagBITMAPINFO; 

El TRGBQuad elemento describe un solo píxel, pero el mapa de bits hace, por supuesto, contiene más de un píxel.Por lo tanto, uno nunca utilizar una variable local de tipo TBitmapInfo, pero siempre un puntero a ella:

var 
    BmpInfo: PBitmapInfo; 
begin 
    // some other code determines width and height... 
    ... 
    BmpInfo := AllocMem(SizeOf(TBitmapInfoHeader) 
    + BmpWidth * BmpHeight * SizeOf(TRGBQuad)); 
    ... 
end; 

Ahora, utilizando el puntero se puede acceder a todos los píxeles, a pesar de que TBitmapInfo no solo tienen una sola. Tenga en cuenta que para dicho código, debe desactivar la comprobación de rango.

Por supuesto, este tipo de cosas también pueden manejarse con la clase 0emrystStream , que es básicamente un envoltorio amigable alrededor de un puntero a un bloque de memoria.

Y, por supuesto, es mucho más fácil crear unTBitmap y asignar su ancho, alto y formato de píxeles. Para decirlo de nuevo, Delphi VCL elimina la mayoría de los casos en los que de otro modo serían necesarios punteros.

punteros a caracteres se pueden utilizar para acelerar las operaciones de cadena

Esto es, como la mayoría de las optimizaciones micro, algo para ser utilizado sólo en casos extremos, después de haber perfilado y ha encontrado el código de uso de cadenas para consumir mucho tiempo.

Una buena propiedad de las cadenas es que son contadas por referencia. Copiarlos no copia la memoria que ocupan, sino que solo aumenta el recuento de referencias. Solo cuando el código intente modificar una cadena que tenga un recuento de referencia mayor que 1 se copiará la memoria, para crear una cadena con un recuento de referencia de 1, que luego se puede modificar de forma segura.

Una propiedad no tan agradable de strings es que son referencias contadas. Cada operación que posiblemente pueda modificar la cadena tiene que asegurarse de que el recuento de referencia sea 1, porque de lo contrario las modificaciones a la cadena serían peligrosas. Reemplazar un personaje en una cadena es una modificación. Para asegurarse de que el recuento de referencias sea 1, el compilador agrega una llamada a UniqueString() cuando se escribe un carácter en una cadena. Y así por escrito n caracteres de una cadena en un bucle causará UniqueString() ser llamado n veces, a pesar de que después de la primera vez que se está seguro de que la cuenta de referencia es 1. Esto significa básicamente n - 1 llamadas de UniqueString() se realizan innecesariamente.

Usar un puntero a los caracteres es una forma común de acelerar las operaciones de cadena que involucran bucles. Imagine que desea (para fines de visualización) reemplazar todos los espacios en una cadena con un pequeño punto. Utilice la vista de la CPU del depurador y comparar el código ejecutado para este código

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString; 
var 
    i: integer; 
begin 
    Result := AValue; 
    for i := 1 to Length(Result) do begin 
    if Result[i] = ' ' then 
     Result[i] := $B7; 
    end; 
end; 

con este código

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString; 
var 
    P: PAnsiChar; 
begin 
    Result := AValue; 
    P := PAnsiChar(Result); 
    while P[0] <> #0 do begin 
    if P[0] = ' ' then 
     P[0] := $B7; 
    Inc(P); 
    end; 
end; 

En la segunda función habrá sólo una llamada a UniqueString(), cuando la dirección del primer carácter de cadena está asignada al puntero de char.