[Estructura de datos] Acerca de las referencias de C ++

Fuente reimpresa: http://www.cnblogs.com/xiaofengkang/


El resumen del tipo grande: uno de los beneficios de las citas es que no se genera ninguna copia en la memoria cuando se llama a la función.

Resumen de cotización

(1) En el uso de la referencia, no tiene sentido simplemente dar un alias a una variable. El propósito de la referencia se utiliza principalmente para resolver el problema de la eficiencia de transferencia insatisfactoria y el espacio de grandes bloques de datos u objetos en la transferencia de parámetros de función .
(2) Pasar parámetros de función por referencia puede garantizar que no se genere ninguna copia durante la transferencia de parámetros, lo que mejora la eficiencia de la transferencia, y el uso de const garantiza la seguridad de la transferencia de referencia.
(3) La diferencia entre referencia y puntero
<1> Después de que un puntero apunta a un objeto a través de una variable de puntero, opera indirectamente sobre la variable a la que apunta. El uso de punteros en el programa hace que el programa sea menos legible;
<2> La referencia en sí es el alias de la variable de destino, y la operación de la referencia es la operación de la variable de destino.
(4) Momento del uso de citas. Se recomienda utilizar referencias para los operadores de flujo << y >>, el valor de retorno del operador de asignación =, los parámetros del constructor de copia, el parámetro del operador de asignación = y otras situaciones.

Una referencia es un alias para una variable (destino) y la operación en la referencia es exactamente la misma que la operación directa en la variable.

Método de declaración referenciado
类型标识符 &引用名=目标变量名;

【例1】:
int a;  
int &ra=a;  //定义引用ra,它是变量a的引用,即别名

(1) & aquí no es para el cálculo de la dirección, sino para la identificación.
(2) El identificador de tipo se refiere al tipo de variable de destino.
(3) Al declarar una referencia, debe inicializarse al mismo tiempo.
(4) Una vez declarada la referencia, es equivalente a que el nombre de la variable de destino tenga dos nombres, a saber, el nombre original del destino y el nombre de la referencia, y el nombre de la referencia no se puede utilizar como alias para otros nombres de variables.

ra=1; 	//等价于 a=1; 

(5) Declarar una referencia no es una nueva definición de una variable, solo significa que el nombre de la referencia es un alias del nombre de la variable de destino, no es un tipo de datos en sí mismo, por lo que la referencia en sí no ocupa la unidad de almacenamiento y el sistema no asigna la referencia. Unidad de almacenamiento. Por tanto: buscar la dirección de la referencia es buscar la dirección de la variable objetivo. & ra es igual a & a.
(6) No se puede establecer la referencia de la matriz. Debido a que una matriz es una colección de varios elementos, es imposible crear un alias para una matriz.
(7) No se puede establecer una referencia a una referencia y no se puede establecer un puntero a una referencia. ¡Porque la referencia no es un tipo de datos! ! Por tanto, no hay referencias a referencias, ni punteros a referencias.

【例如】:
int n;
int &&r=n;//错误,编译系统把"int &"看成一体,把"&r"看成一体,即建立了引用的引用,引用的对象应当是某种数据类型的变量
int &*p=n;//错误,编译系统把"int &"看成一体,把" *p "看成一体,即建立了指向引用的指针,指针只能指向某种数据类型的变量

(8) Vale la pena mencionar que puede crear referencias de puntero

【例如】:
int *p;
int *&q=p;//正确,编译系统把" int * "看成一体,把"&q"看成一体,即建立指针p的引用,亦即给指针p起别名q。

Aplicación de referencia

1. Referencia como parámetro

Un papel importante de referencia es como parámetro de función. En el lenguaje C anterior, la transferencia de parámetros de función era transferencia de valor. Si se pasa un gran bloque de datos como parámetro, la solución utilizada suele ser un puntero, porque esto puede evitar empujar todo el bloque de datos en la pila y mejorar la eficiencia del programa. Pero ahora (en C ++) se ha agregado una opción igualmente eficiente (y una opción necesaria en algunos casos especiales), que es referencia.

【例2】:
void swap(int &p1,  int &p2) {
    
    //此处函数的形参p1, p2都是引用 
	int p;
	p=p1;
	p1=p2;
	p2=p;
} 

Para llamar a la función en el programa, el pulsador de la función de llamada principal correspondiente se puede llamar directamente con la variable como parámetro real, sin ningún requisito especial para la variable de parámetro real.
Por ejemplo, correspondiente a la función de intercambio definida anteriormente, la función de llamada principal correspondiente se puede escribir como:

main(){
    
     
 int a,b;
 cin>>a>>b; 			//输入a,b两变量的值
 swap(a,b); 			//直接以变量a和b作为实参调用swap函数 
 cout<<a<< ' ' <<b; 	//输出结果 
}
上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10。

由【例2】可看出:
(1) El efecto de pasar una referencia a una función es el mismo que pasar un puntero. En este momento, el parámetro formal de la función llamada se convierte en un alias de la variable de parámetro real u objeto en la función de llamada original a usar, por lo que la operación de la variable de parámetro formal en la función llamada es a su objeto de destino correspondiente (en la función de llamada). En la función de llamada).
(2) El uso de referencia para pasar los parámetros de la función no produce una copia de los parámetros reales en la memoria, es una operación directa sobre los parámetros reales; y el uso de variables generales para pasar los parámetros de la función, cuando ocurre una llamada a la función, es necesario asignar almacenamiento a los parámetros formales. Unidad, la variable de parámetro formal es una copia de la variable de parámetro real; si se pasa el objeto, también se llamará al constructor de copia. Por tanto, cuando los datos que pasa el parámetro son grandes, la eficiencia y el espacio que ocupa la referencia son mejores que la variable general.
(3) Aunque el uso de punteros como parámetros de función también puede lograr el efecto de usar referencias, en la función llamada, las unidades de almacenamiento también deben asignarse a parámetros formales, y la forma de "* nombre de variable de puntero" debe usarse repetidamente para los cálculos. Esto es propenso a errores y poca legibilidad del programa; por otro lado, en el punto de llamada de la función que llama, la dirección de la variable debe usarse como el parámetro real. La cita es más fácil de usar y más clara.

void fun(int * a,int * b){
    
    
	int t = (*a);
	*a = *b;
	*b = t;
}

La función de tecla correspondiente es:

int main(){
    
    
	int i = 1,j = 2;
	fun(&i,&j);
	cout<<i<<" "<<j<<endl;
	return 0;
}

Si desea usar referencias para mejorar la eficiencia del programa, pero también para proteger los datos pasados ​​a la función para que no se modifiquen en la función, debe usar referencias constantes.

2, citado a menudo

Declaración a menudo citada

const 类型标识符 &引用名=目标变量名;

La referencia declarada de esta manera no puede modificar el valor de la variable objetivo por referencia, de modo que el objetivo de la referencia se convierte en constante, lo que logra la seguridad de la referencia.

【例3】: 
int a ;
const int &ra=a;
ra=1;		//错误,不能通过引用对目标变量的值进行修改
a=1;		//正确 

Esto no es solo para hacer que el código sea más robusto, sino que también tiene otras necesidades.

【例4】:假设有如下函数声明:
string foo();
void bar(string & s);

Entonces la siguiente expresión será ilegal:

bar(foo());
bar("hello world"); 

La razón es que las cadenas foo () y "hello world" producirán un objeto temporal, y en C ++, estos objetos temporales son de tipo constante. Por lo tanto, la expresión anterior es un intento de convertir un objeto de tipo constante en un tipo no constante, lo cual es ilegal.

引用型参数应该在能被定义为const的情况下,尽量定义为const

3. Referencia como valor de retorno

Para devolver el valor de la función por referencia, la definición de la función debe estar en el siguiente formato: Descripción: (1) Devuelve el valor de la función por referencia y agrega & antes del nombre de la función al definir la función. (2) La mayor ventaja de devolver un valor de función por referencia es que, No se realiza ninguna copia del valor devuelto en la memoria.
类型标识符 &函数名(形参列表及类型说明)
{函数体}



【例5】: 以下程序中定义了一个普通的函数fn1(它用返回值的方法返回函数值),另外一个函数fn2,它以引用的方法返回函数值。

#include <iostream.h>

float  temp;				//定义全局变量temp
float  fn1(float r);		//声明函数fn1
float  &fn2(float r);		//声明函数fn2
float  fn1(float r);		//声明函数fn1

float  fn1(float r){
    
     		//定义函数fn1,它以返回值的方法返回函数值
	temp=(float)(r*r*3.14); 
	return temp; 
}

float &fn2(float r){
    
     		//定义函数fn2,它以引用方式返回函数值
	temp=(float)(r*r*3.14); 
	return temp;
}

void main() {
    
     				//主函数
	float a=fn1(10.0); 		//第1种情况,系统生成要返回值的副本(即临时变量)
	float &b=fn1(10.0); 	//第2种情况,可能会出错(不同 C++系统有不同规定)
 //不能从被调函数中返回一个临时变量或局部变量的引用
	float c=fn2(10.0); 		//第3种情况,系统不生成返回值的副本
 //可以从被调函数中返回一个全局变量的引用
	float &d=fn2(10.0); 	//第4种情况,系统不生成返回值的副本
 //可以从被调函数中返回一个全局变量的引用
	cout<<a<<c<<d;
} 

引用作为返回值,必须遵守以下规则:
(1) No se pueden devolver referencias a variables locales. Este artículo puede hacer referencia al artículo 31 de C ++ efectivo [1]. La razón principal es que las variables locales se destruirán después de que regrese la función, por lo que la referencia devuelta se convierte en una referencia "no referida" y el programa entrará en un estado desconocido.
(2) No se puede devolver una referencia a la memoria asignada por nuevo dentro de la función. Este artículo puede hacer referencia al artículo 31 de C ++ efectivo [1]. Aunque no hay destrucción pasiva de variables locales, pero para esta situación (devolviendo una referencia a la nueva memoria asignada dentro de la función), se enfrenta a otras situaciones embarazosas. Por ejemplo, si la referencia devuelta por la función solo aparece como una variable temporal, pero no está asignada a una variable real, entonces el espacio al que apunta esta referencia (asignado por nuevo) no se puede liberar, lo que provoca una pérdida de memoria.
(3) Las referencias a los miembros de la clase se pueden devolver, pero const es lo mejor. Este principio puede referirse al artículo 30 de C ++ efectivo [1]. La razón principal es que cuando los atributos de un objeto están asociados con una determinada regla de negocio, su asignación suele estar relacionada con otros atributos o con el estado del objeto, por lo que es necesario encapsular la operación de asignación en una regla de negocio. Si otros objetos pueden obtener una referencia (o puntero) no constante del atributo, entonces la simple asignación del atributo destruirá la integridad de las reglas comerciales.
(4) Sobrecarga de referencias y algunos operadores:
Operadores de flujo << y >>, a menudo se espera que estos dos operadores se usen consecutivamente, por ejemplo: cout << "hola" << endl; Por lo tanto, el valor de retorno de estos dos operadores debe ser uno y aún admitir estas dos operaciones La referencia de flujo del símbolo. Otras soluciones opcionales incluyen: devolver un objeto de flujo y devolver un puntero de objeto de flujo. Pero para devolver un objeto de flujo, el programa debe reconstruir (copiar) un nuevo objeto de flujo, es decir, ¡los dos operadores << consecutivos son en realidad para objetos diferentes! Esto es inaceptable. Para devolver un puntero de flujo, el operador << no se puede utilizar de forma continua. Por lo tanto, devolver una referencia de objeto de flujo es la única opción. Esta única elección es muy importante, ilustra la importancia y la irremplazabilidad de las referencias, quizás por eso se introduce el concepto de referencia en el lenguaje C ++. Operador de asignación =. Este operador, como el operador de flujo, se puede utilizar de forma continua, por ejemplo: x = j = 10; o (x = 10) = 100; el valor de retorno del operador de asignación debe ser un valor l para que pueda asignarse. Por lo tanto, la referencia se convierte en la única opción de valor de retorno para este operador.

【例6】 测试用返回引用的函数值作为赋值表达式的左值。
#i nclude <iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main(){
    
    
put(0)=10;  //以put(0)函数值作为左值,等价于vals[0]=10; 
put(9)=20;  //以put(9)函数值作为左值,等价于vals[9]=10; 
cout<<vals[0]; 
cout<<vals[9];
}


int &put(int n){
    
    
	if (n>=0 && n<=9 )
		return vals[n]; 
	else 
		cout<<"subscript error"; return error;
}

(5) En algunos otros operadores, nunca devuelva referencias: + - * / Cuatro operadores aritméticos. No pueden devolver referencias, el artículo 23 de Effective C ++ [1] analiza este problema en detalle. La razón principal es que estos cuatro operadores no tienen efectos secundarios. Por lo tanto, deben construir un objeto como valor de retorno. Las soluciones opcionales incluyen: devolver un objeto, devolver una referencia a una variable local, devolver una referencia a un objeto asignado por nuevo y devolver Una referencia de objeto estático. De acuerdo con las tres reglas de referencia antes mencionadas como valor de retorno, la segunda y tercera opción fueron rechazadas. Las referencias a objetos estáticos también causan errores porque ((a + b) == (c + d)) siempre será verdadero. Entonces, la única opción que queda es devolver un objeto.

4. Citas y polimorfismo

La referencia es otro medio además de los punteros que pueden producir efectos polimórficos. Esto significa que una referencia a una clase base puede apuntar a una instancia de su clase derivada.

【例7】: 
class A;
class B:public A{
    
    ……};
B b;
A &Ref = b;  // 用派生类对象初始化基类对象的引用

Ref solo se puede usar para acceder a miembros del objeto de clase derivada heredado de la clase base, y la referencia de la clase base apunta a la clase derivada. Si se define una función virtual en la clase A y la función virtual se reescribe en la clase B, se pueden producir efectos polimórficos a través de la Ref.

Supongo que te gusta

Origin blog.csdn.net/qq_44714521/article/details/107006806
Recomendado
Clasificación