Soy nuevo en la programación de C ++, pero tengo experiencia en Java. Necesito orientación sobre cómo pasar objetos a funciones en C ++.
¿Necesito pasar punteros, referencias o valores sin puntero y sin referencia? Recuerdo que en Java no hay tales problemas ya que pasamos solo la variable que contiene referencia a los objetos.
Sería genial si también pudiera explicar dónde usar cada una de esas opciones.
c++
pointers
pass-by-reference
pass-by-value
c++-faq
Rakesh K
fuente
fuente
Respuestas:
Reglas generales para C ++ 11:
Pase por valor , excepto cuando
const
referencia ,const
referencia que no sea de valor ,const
referencia o no).Pasar por puntero prácticamente nunca se aconseja. Los parámetros opcionales se expresan mejor como a
std::optional
(boost::optional
para bibliotecas estándar más antiguas), y el aliasing se hace bien por referencia.La semántica de movimiento de C ++ 11 hace que pasar y regresar por valor sea mucho más atractivo incluso para objetos complejos.
Reglas prácticas para C ++ 03:
Pase argumentos por
const
referencia , excepto cuandoconst
referenciaNULL
/0
/ en sunullptr
lugar; aplique la regla anterior para determinar si debe pasar por un puntero a unconst
argumento(aquí, "pasar por valor" se llama "pasar por copia", porque pasar por valor siempre crea una copia en C ++ 03)
Hay más en esto, pero estas pocas reglas para principiantes te llevarán bastante lejos.
fuente
Existen algunas diferencias en las convenciones de llamadas en C ++ y Java. En C ++, técnicamente solo hay dos convenciones: paso por valor y paso por referencia, con cierta literatura que incluye una tercera convención de paso por puntero (que en realidad es paso por valor de un tipo de puntero). Además de eso, puede agregar constancia al tipo de argumento, mejorando la semántica.
Pase por referencia
Pasar por referencia significa que la función recibirá conceptualmente su instancia de objeto y no una copia de la misma. La referencia es conceptualmente un alias del objeto que se utilizó en el contexto de la llamada y no puede ser nulo. Todas las operaciones realizadas dentro de la función se aplican al objeto fuera de la función. Esta convención no está disponible en Java o C.
Pasar por valor (y pasar por puntero)
El compilador generará una copia del objeto en el contexto de la llamada y usará esa copia dentro de la función. Todas las operaciones realizadas dentro de la función se realizan en la copia, no en el elemento externo. Esta es la convención para tipos primitivos en Java.
Una versión especial de él es pasar un puntero (dirección del objeto) a una función. La función recibe el puntero, y todas y cada una de las operaciones aplicadas al puntero en sí se aplican a la copia (puntero), por otro lado, las operaciones aplicadas al puntero desreferenciado se aplicarán a la instancia del objeto en esa ubicación de memoria, por lo que la función puede tener efectos secundarios El efecto de usar el paso por valor de un puntero al objeto permitirá que la función interna modifique los valores externos, como con el paso por referencia y también permitirá valores opcionales (pasar un puntero nulo).
Esta es la convención usada en C cuando una función necesita modificar una variable externa, y la convención usada en Java con tipos de referencia: la referencia se copia, pero el objeto referido es el mismo: los cambios en la referencia / puntero no son visibles fuera la función, pero los cambios en la memoria puntiaguda son.
Agregar const a la ecuación
En C ++ puede asignar constancia a los objetos al definir variables, punteros y referencias en diferentes niveles. Puede declarar que una variable es constante, puede declarar una referencia a una instancia constante y puede definir todos los punteros a objetos constantes, punteros constantes a objetos mutables y punteros constantes a elementos constantes. Por el contrario, en Java solo puede definir un nivel de constancia (palabra clave final): el de la variable (instancia para tipos primitivos, referencia para tipos de referencia), pero no puede definir una referencia a un elemento inmutable (a menos que la clase en sí sea inmutable).
Esto se usa ampliamente en convenciones de llamadas de C ++. Cuando los objetos son pequeños, puede pasar el objeto por valor. El compilador generará una copia, pero esa copia no es una operación costosa. Para cualquier otro tipo, si la función no cambiará el objeto, puede pasar una referencia a una instancia constante (generalmente llamada referencia constante) del tipo. Esto no copiará el objeto, sino que lo pasará a la función. Pero al mismo tiempo, el compilador garantizará que el objeto no se cambie dentro de la función.
Reglas de juego
Estas son algunas reglas básicas a seguir:
Hay otras pequeñas desviaciones de estas reglas, la primera de las cuales es manejar la propiedad de un objeto. Cuando un objeto se asigna dinámicamente con nuevo, se debe desasignar con eliminar (o las versiones [] del mismo). El objeto o función responsable de la destrucción del objeto se considera el propietario del recurso. Cuando se crea un objeto asignado dinámicamente en un fragmento de código, pero la propiedad se transfiere a un elemento diferente, generalmente se hace con una semántica de paso por puntero, o si es posible con punteros inteligentes.
Nota al margen
Es importante insistir en la importancia de la diferencia entre las referencias de C ++ y Java. En C ++, las referencias son conceptualmente la instancia del objeto, no un descriptor de acceso al mismo. El ejemplo más simple es implementar una función de intercambio:
La función de intercambio anterior cambia sus argumentos mediante el uso de referencias. El código más cercano en Java:
La versión Java del código modificará las copias de las referencias internamente, pero no modificará los objetos reales externamente. Las referencias Java son punteros en C sin aritmética de punteros que se pasan por valor a las funciones.
fuente
Hay varios casos a considerar.
Parámetro modificado (parámetros "out" y "in / out")
Este caso se trata principalmente de estilo: ¿desea que el código se vea como call (obj) o call (& obj) ? Sin embargo, hay dos puntos donde la diferencia es importante: el caso opcional, a continuación, y desea utilizar una referencia al sobrecargar operadores.
... y opcional
Parámetro no modificado
Este es el caso interesante. La regla general es que los tipos "baratos de copiar" se pasan por valor, generalmente son tipos pequeños (pero no siempre), mientras que otros se pasan por const ref. Sin embargo, si necesita hacer una copia dentro de su función, debe pasar por valor . (Sí, esto expone un poco de detalle de implementación. C'est le C ++. )
... y opcional
Aquí hay la menor diferencia entre todas las situaciones, así que elige la que te haga la vida más fácil.
Const por valor es un detalle de implementación
¡Estas declaraciones son en realidad exactamente la misma función! Al pasar por valor, const es puramente un detalle de implementación. Pruébalo:
fuente
const
ser una implementación al pasar por valor.Pase por valor:
Pase las variables por valor cuando la función necesite un aislamiento completo del entorno, es decir, para evitar que la función modifique la variable original, así como para que otros hilos modifiquen su valor mientras se ejecuta la función.
La desventaja son los ciclos de la CPU y la memoria adicional gastada para copiar el objeto.
Pase por referencia constante:
Este formulario emula el comportamiento de paso por valor mientras elimina la sobrecarga de copia. La función obtiene acceso de lectura al objeto original, pero no puede modificar su valor.
La desventaja es la seguridad del subproceso: cualquier cambio realizado en el objeto original por otro subproceso aparecerá dentro de la función mientras aún se está ejecutando.
Pase por referencia no constante:
Use esto cuando la función tenga que volver a escribir algún valor en la variable, que en última instancia será utilizada por la persona que llama.
Al igual que el caso de referencia constante, esto no es seguro para subprocesos.
Pase por puntero constante:
Funcionalmente igual que pasar por referencia constante, excepto por la sintaxis diferente, más el hecho de que la función de llamada puede pasar el puntero NULL para indicar que no tiene datos válidos para pasar.
No es seguro para subprocesos.
Pase por puntero no constante:
Similar a la referencia no constante. La persona que llama generalmente establece la variable en NULL cuando se supone que la función no debe reescribir un valor. Esta convención se ve en muchas API de glibc. Ejemplo:
Al igual que todos pasan por referencia / puntero, no es seguro para subprocesos.
fuente
Como nadie mencionó que estoy agregando, cuando pasa un objeto a una función en c ++, se llama al constructor de copia predeterminado del objeto si no tiene uno que cree un clon del objeto y luego lo pase al método, entonces cuando cambia los valores del objeto que se reflejarán en la copia del objeto en lugar del objeto original, ese es el problema en c ++, por lo tanto, si hace que todos los atributos de clase sean punteros, los constructores de copia copiarán las direcciones de atributos de puntero, por lo que cuando las invocaciones de métodos en el objeto que manipula los valores almacenados en las direcciones de atributos de puntero, los cambios también se reflejan en el objeto original que se pasa como parámetro, por lo que esto puede comportarse igual que Java, pero no olvide que toda su clase los atributos deben ser punteros, también debe cambiar los valores de los punteros,será muy claro con la explicación del código.
Pero esta no es una buena idea, ya que terminarás escribiendo mucho código relacionado con punteros, que son propensos a pérdidas de memoria y no olvides llamar a los destructores. Y para evitar que c ++ tenga constructores de copia donde creará nueva memoria cuando los objetos que contienen punteros se pasen a argumentos de función que dejarán de manipular los datos de otros objetos, Java pasa por valor y el valor es referencia, por lo que no requiere constructores de copia.
fuente
Hay tres métodos para pasar un objeto a una función como parámetro:
Ve a través del siguiente ejemplo:
Salida:
fuente
Las siguientes son las formas de pasar argumentos / parámetros para funcionar en C ++.
1. por valor.
2. por referencia.
3. por objeto.
fuente