Pregunta

Soy nuevo en la programación C ++, pero tengo experiencia en Java. Necesito orientación sobre cómo pasar objetos a funciones en C ++.

¿Necesito pasar punteros, referencias, o valores no-puntero y no son de referencia? Recuerdo en Java no existen tales problemas, ya que pasamos sólo la variable que contiene referencia a los objetos.

Sería muy bueno si también podría explicar dónde utilizar cada una de esas opciones.

¿Fue útil?

Solución

Reglas de pulgar para C ++ 11:

Pass por valor , excepto cuando

  1. usted no necesita la propiedad del objeto y un alias sencilla va a hacer, en cuyo caso pasa por referencia const ,
  2. debe mutar el objeto, en cuyo caso, el uso pasar por una referencia no const lvalue ,
  3. se pasa objetos de clases derivadas como clases base, en cuyo caso se necesita para el paso por referencia . (Use las reglas anteriores para determinar si se pasa por referencia const o no.)

Al pasar por el puntero se encuentra prácticamente nunca se recomienda. Los parámetros opcionales se expresan mejor como una std::optional (boost::optional en las librerías std mayores), y aliasing está muy bien hecho referencia.

C ++ de 11 semántica movimiento hacen pasar o retornar por valor mucho más atractivo incluso para objetos complejos.


Las reglas generales para C ++ 03:

argumentos Pass por referencia const , excepto cuando

  1. han de ser cambiado dentro de la función y tales cambios deben reflejarse exterior, en cuyo caso el paso por referencia no const
  2. la función debe ser exigible sin ningún argumento, en cuyo caso se pasa por el puntero, por lo que los usuarios pueden pasar NULL / 0 / nullptr lugar; aplicar la regla anterior para determinar si debe pasar por un puntero a un argumento const
  3. son de los tipos predefinidos, que puede ser aprobada por copia
  4. han de ser cambiado dentro de la función y tales cambios deben no fuera de reflejarse, en cuyo caso se puede pasar por copia (una alternativa sería pasar de acuerdo con las reglas anteriores y hacer una copia en el interior de la función)

(en este caso, "paso por valor" se llama "pasar de copia", debido a que pasa por el valor siempre crea una copia en C ++ 03)


Hay más a esto, pero las reglas de estos pocos principiantes te llevará muy lejos.

Otros consejos

Hay algunas diferencias en las convenciones de llamada en C ++ y Java. En C ++ no están técnicamente hablando sólo dos convenciones: pase por valor y pasar por referencia, con un poco de literatura incluyendo tercera convención pase-por-puntero (que en realidad está el paso por valor de un tipo de puntero). Además de eso, se puede añadir const-dad con el tipo del argumento, la mejora de la semántica.

Pasar por referencia

Al pasar por los medios de referencia que la función conceptualmente recibir su instancia de objeto y no una copia de la misma. La referencia es conceptualmente un alias para el objeto que se utiliza en el contexto de 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.

paso por valor (y el paso por puntero)

El compilador generará una copia del objeto en el contexto llamar y usar dicha copia dentro de la función. Todas las operaciones realizadas dentro de la función se realizan para la copia, no el elemento externo. Esta es la convención para los tipos primitivos de Java.

Una versión especial de que está pasando un puntero (dirección-del objeto) en una función. La función recibe el puntero, y cualquiera y todas las operaciones aplica al propio puntero se aplican a la copia (puntero), por otra parte, las operaciones aplicadas al puntero sin referencia se aplicará a la instancia del objeto en esa ubicación de memoria, por lo que la función pueden tener efectos secundarios. El efecto de usar pase por valor de un puntero al objeto permitirá que la función interna para modificar valores externos, como con pase por referencia y también permitirá que para valores opcionales (pasar un puntero null).

Esta es la convención utilizada en C cuando una función necesita modificar una variable externa, y la convención utilizada en Java con tipos de referencia: la referencia se copia, pero el objeto a que se refiere es el mismo: los cambios en la referencia / puntero son no fuera visible la función, pero los cambios en la memoria en punta son.

Adición const a la ecuación

En C ++ se puede asignar constante-dad a la hora de definir los objetos variables, punteros y referencias a diferentes niveles. Se puede declarar que una variable sea constante, se puede declarar una referencia a una instancia constante, y se puede definir todos los punteros a objetos constantes, punteros constantes de los objetos mutables y punteros constantes a elementos constantes. Por el contrario en Java sólo se puede definir un nivel de constante-dad (palabra clave final): el de la variable (ejemplo para los tipos primitivos, de referencia para los tipos de referencia), pero no se puede definir una referencia a un elemento inmutable (a menos que la clase misma es inmutable).

Esto se utiliza ampliamente en C ++ convenciones de llamada. 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 va a cambiar el objeto, puede pasar una referencia a una instancia constante (generalmente llamado constante referencia) del tipo. Esto no va a copiar el objeto, pero pasarlo a la función. Pero al mismo tiempo, el compilador garantiza que el objeto no se cambia dentro de la función.

Reglas de pulgar

Estos son algunas reglas básicas a seguir:

  • Prefiero pase por valor para los tipos primitivos
  • Prefiero pase por referencia con referencias a constante para otros tipos
  • Si las necesidades de función para modificar el uso argumento pase por referencia
  • Si el argumento es opcional, el uso pase por puntero (a constante si el valor opcional no debe ser modificado)

Hay otras pequeñas desviaciones de estas reglas, el primero de los cuales es el manejo de la propiedad de un objeto. Cuando un objeto se asigna dinámicamente con nuevo, debe ser desasignado con delete (o las versiones [] de los mismos). El objeto o function es responsable de la destrucción del objeto es considerado el propietario del recurso. Cuando se crea un objeto asignado dinámicamente en una pieza de código, pero la propiedad se transfiere a un elemento diferente que por lo general se lleva a cabo con la semántica de pase-a-puntero, o si es posible con punteros inteligentes.

Nota al margen

Es importante insistir en la importancia de la diferencia entre las referencias C ++ y Java. En C ++ referencias son conceptualmente la instancia del objeto, no un descriptor de acceso a la misma. El ejemplo más simple está implementando una función de intercambio:

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

La función de intercambio por encima de cambios tanto sus argumentos a través del uso de referencias. El código en Java más cercano:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

La versión Java de código modifica las copias de las referencias internamente, pero no modificará los objetos reales externamente. referencias de Java son los punteros de C sin la aritmética de punteros que van pasando por el valor en funciones.

Hay varios casos a considerar.

parámetro modificado ( "fuera" y "in / out" parámetros)

void modifies(T &param);
// vs
void modifies(T *param);

Este caso es sobre todo cuestión de estilo: ¿quieres el código para que parezca llamada (obj) o llamada (y obj) ? Sin embargo, hay dos puntos en los que las cuestiones de diferencia:. El caso opcional, a continuación, y desea utilizar una referencia cuando la sobrecarga de operadores

... y opcional

void modifies(T *param=0);  // default value optional, too
// vs
void modifies();
void modifies(T &param);

El parámetro no modificado

void uses(T const &param);
// vs
void uses(T param);

Este es el caso interesante. La regla de oro es "barato para copiar" tipos se pasan por valor - estos son generalmente pequeños tipos (pero no siempre) - mientras que otros se pasan por ref const. Sin embargo, si usted necesita para hacer una copia dentro de su función sin tener en cuenta, que debe pasar por valor . (Sí, esto expone un poco de detalle de implementación. C'est le C ++. )

... y opcional

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // or optional(T param)

No es la menor diferencia aquí entre todas las situaciones, por lo que elegir lo que hace la vida más fácil.

Const por valor es un detalle de implementación

void f(T);
void f(T const);

Estas declaraciones son en realidad el exacta misma función! Al pasar por valor, const es meramente un detalle de implementación. Pruébalo:

void f(int);
void f(int const) { /* implements above function, not an overload */ }

typedef void NC(int);       // typedefing function types
typedef void C(int const);

NC *nc = &f;  // nc is a function pointer
C *c = nc;    // C and NC are identical types

paso por valor:

void func (vector v)

variables de paso por valor cuando la función necesita el aislamiento completo del medio ambiente es decir, para evitar que la función de la modificación de la variable original, así como para evitar que otros hilos de la modificación de su valor mientras se está ejecutando la función.

La desventaja es los ciclos de CPU y memoria adicional gastado para copiar el objeto.

Pasar por referencia const:

void func (const vector& v);

Esta forma emula pasan por valor comportamiento mientras se quita la sobrecarga de copia. La función tiene acceso de lectura al objeto original, pero no puede modificar su valor.

La desventaja es la seguridad hilo:. Cualquier cambio realizado en el objeto original por otro subproceso se mostrará dentro de la función mientras aún está ejecutando

Pasar por referencia no const:

void func (vector& v)

Utilice esta función cuando la tiene que escribir de nuevo algún valor a la variable, que en última instancia acostumbrarse por la persona que llama.

Al igual que el caso de referencia const, esto no es seguro para subprocesos.

Paso de puntero constante:

void func (const vector* vp);

Funcionalmente mismo que pase por const referencia a excepción de la sintaxis diferente, más el hecho de que la función de llamada puede pasar puntero NULL para indicar que no tiene datos válidos para pasar.

No subprocesos.

Paso de puntero no constante:

void func (vector* vp);

Al igual que en referencia no const. La persona que llama normalmente establece la variable a NULL cuando no se supone que la función de volver a escribir un valor. Esta convención se ve en muchas API glibc. Ejemplo:

void func (string* str, /* ... */) {
    if (str != NULL) {
        *str = some_value; // assign to *str only if it's non-null
    }
}

Al igual que todos pasan por referencia / puntero, no apta para subprocesos.

Dado que nadie ha mencionado que estoy añadiendo en él, cuando se pasa un objeto a una función en C ++ el constructor de copia por defecto del objeto se llama si no tiene uno que crea un clon del objeto y luego pasarlo a la método, por lo que cuando se cambian los valores de los objetos que reflejen en la copia del objeto en lugar del objeto original, que es el problema en C ++, así que si usted hace todos los atributos de clase para ser punteros, a continuación, los constructores de copia copiará el direcciones de los atributos de puntero, por lo que cuando las invocaciones de métodos en el objeto que manipula los valores almacenados en puntero atributos direcciones, los cambios también se reflejan en el objeto original que se pasa como un parámetro, por lo que este puede comportarse misma un Java, pero no se olvide de que todos sus atributos de clase deben ser punteros, también debe cambiar los valores de los punteros, será mucho más clara con la explicación del código.

Class CPlusPlusJavaFunctionality {
    public:
       CPlusPlusJavaFunctionality(){
         attribute = new int;
         *attribute = value;
       }

       void setValue(int value){
           *attribute = value;
       }

       void getValue(){
          return *attribute;
       }

       ~ CPlusPlusJavaFuncitonality(){
          delete(attribute);
       }

    private:
       int *attribute;
}

void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
   int* prt = obj.attribute;
   *ptr = value;
}

int main(){

   CPlusPlusJavaFunctionality obj;

   obj.setValue(10);

   cout<< obj.getValue();  //output: 10

   changeObjectAttribute(obj, 15);

   cout<< obj.getValue();  //output: 15
}

Pero esto no es buena idea ya que se termina de escribir mucho código que involucra a los punteros, que son propensos a pérdidas de memoria y no se olvide de llamar a los destructores. Y para evitar esto C ++ tiene constructores de copia en el que creará la nueva memoria cuando los objetos que contienen punteros se pasan a los argumentos de funciones que detendrán la manipulación de datos de otros objetos, Java no pase por el valor y el valor es de referencia, por lo que no requieren copiar constructores.

Hay tres métodos de pasar un objeto a una función como un parámetro:

  1. Pasar por referencia
  2. paso por valor
  3. adición constante en el parámetro

Go a través del siguiente ejemplo:

class Sample
{
public:
    int *ptr;
    int mVar;

    Sample(int i)
    {
        mVar = 4;
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }

    void PrintVal()
    {
        cout << "The value of the pointer is " << *ptr << endl
             << "The value of the variable is " << mVar;
   }
};

void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}


int main()
{

  Sample s1= 10;
  SomeFunc(s1);
  s1.PrintVal();
  char ch;
  cin >> ch;
}

Salida:

  

decir que estoy en someFunc
  El valor del puntero es -17891602
  El valor de la variable es 4

Las siguientes son las formas de pasar argumentos a / parámetros a la función en C ++.

1. por valor.

// passing parameters by value . . .

void foo(int x) 
{
    x = 6;  
}

2. por referencia.

// passing parameters by reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  
}

// passing parameters by const reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  // compile error: a const reference cannot have its value changed!
}

3. por objeto.

class abc
{
    display()
    {
        cout<<"Class abc";
    }
}


// pass object by value
void show(abc S)
{
    cout<<S.display();
}

// pass object by reference
void show(abc& S)
{
    cout<<S.display();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top