[Notas de estudio básico de C++] Referencia de gramática de C++


cita

concepto de referencia

La referencia no es una nueva definición de una variable, sino un alias para una variable existente. El compilador no abrirá espacio de memoria para la variable de referencia, comparte el mismo espacio de memoria con la variable a la que hace referencia.

Por ejemplo: Li Kui, conocido como "Iron Bull" en casa, y "Black Whirlwind"
inserte la descripción de la imagen aquí
en el mundo, que también es la forma de alias.


Cómo usar las citas:

tipo y nombre de variable de referencia (nombre de objeto) = entidad de referencia;

#include<iostream>
using namespace std;
void TestRef()
{
    
    
	int a = 10;
	int& ra = a;//定义引用类型
	cout << a << endl;
	cout << ra << endl;
}
int main()
{
    
    
	TestRef();
	return 0;
}

inserte la descripción de la imagen aquí
Nota: El tipo de referencia debe ser el mismo tipo que la entidad de referencia

& en este lugar representa una referencia, no una dirección, un operador tiene múltiples significados, es decir, sobrecarga de operadores.
Tenga en cuenta la distinción:
tipo & significa referencia (& después del tipo, como int& b = a;)
& variable significa tomar la dirección (& antes de la variable, como int* pa = &a;)
No existe una relación necesaria entre el dos.

inserte la descripción de la imagen aquí


Propiedades de referencia

1) Una referencia debe inicializarse cuando se define
2) Una variable puede tener varias referencias
3) Una vez que se hace referencia a una entidad, no puede hacer referencia a otras entidades (el objeto al que se hace referencia no se puede cambiar)

#include<iostream>
using namespace std;
void TestRef()
{
    
    
	int a = 10;
	int b = 20;
	//int& ra;//该条语句编译时会报错
	int& ra = a;//定义引用变量的时候就需要给其赋初始值
	int& rra = a;//一个变量可以有多个引用
	ra = b;//不能更改引用对象,这句实际上是将b的值赋值给ra

	cout << a << endl;
	cout << ra << endl;
	cout << rra << endl;
}
int main()
{
    
    
	TestRef();
	return 0;
}

inserte la descripción de la imagen aquí


a menudo citado

void TestConstRef()
{
    
    
	const int a = 10;
	//int& ra = a;//该语句编译时会出错,a为常量
	const int& ra = a;
	//int& b = 10;//该语句编译时会出错,b为常量
	const int& b = 10;
	double d = 12.34;
	//int& rd = d;l/该语句编译时会出错,类型不同
	const int& rd = d;
}

Cuando a es de tipo const int, significa que a es una variable constante, y la variable constante tiene atributos constantes --- el valor no se puede cambiar. En este momento, si usa int & para referirse a a, obtendrá un alias de tipo int, que es común La variable de a puede modificarse, puede cambiar el valor de a, lo que resulta en un conflicto de que se modifica una variable constante. Entonces, para evitar este conflicto, al hacer referencia a una variable de tipo const int, debe usar const int& b = a;

Resumen: cuando la referencia tiene un alias, la autoridad de acceso variable se puede reducir (como const int -> int), pero no ampliar (como const int -> int).
Los permisos no se pueden ampliar: las variables de tipo const no se pueden dar alias de tipos no const. Los
permisos se pueden reducir: es decir, las variables de tipos no const se pueden dar alias de tipos no const y alias de tipos const.

Sugerencia: minificación y ampliación de permisos, solo para referencias y punteros

const int a = 10;
int* p = &a;//这种不行,权限的放大
const int* pa = &a;//需要这种形式

int c = 1;
const int* pc = &c;//可以,属于权限的缩小

Tenga en cuenta la distinción:

const int a = 10;
int& b = a;//这种是不行的,b是a的别名

const int x = 10;
int y = x;//这种是可以的,y和x没什么关系
//这种是不受影响的
#include<iostream>
using namespace std;
int main()
{
    
    
	int i = 1;
	double d = i;//隐式类型转换
	double& ri = i;//可以这样取别名吗? error
	const double& rri = i;//这样呢?ok
	return 0;
}

double d = I; Esta oración es para generar primero una variable temporal
doble double& ri = I; También es una variable temporal que primero generará un tipo doble, y luego se referencia realmente la variable temporal, y esta variable temporal tiene propiedades constantes , por lo que no es posible usar double& directamente, y está bien agregar const.
inserte la descripción de la imagen aquí


Escenas a utilizar

1. Hacer parámetros (①parámetros de salida ②mejorar la eficiencia)

#include<iostream>
using namespace std;
void Swap(int& left, int& right)
{
    
    
	int temp = left;
	left = right;
	right = temp;
}

int main()
{
    
    
	int a = 1;
	int b = 2;
	Swap(a, b);
	cout << a << " " << b << endl;
	return 0;
}

inserte la descripción de la imagen aquí
P: ¿No significa que se requiere inicialización cuando se hace referencia a una definición? ¿No hay inicialización aquí?
R: La referencia en este lugar no es una definición, ¿cuándo es una definición? La definición es cuando se pasa el parámetro, y la operación de inicialización se realizará cuando se pase el parámetro.
inserte la descripción de la imagen aquí
Por supuesto, esto también se puede lograr mediante punteros:

#include<iostream>
using namespace std;
void Swap(int* pa, int* pb)
{
    
    
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
int main()
{
    
    
	int a = 1;
	int b = 2;
	Swap(&a, &b);
	cout << a << " " << b << endl;
	return 0;
}

inserte la descripción de la imagen aquí
2. Hacer el valor de retorno (① Mejorar la eficiencia ② Hablaré de eso más tarde)

int& Count()
{
    
    
	static int n = 0;
	n++;
	// ...
	return n;
}

inserte la descripción de la imagen aquí
Cualquier método de paso por valor (parámetro que pasa por valor, paso por valor y devolución) generará una copia de la variable temporal. Pass-by-reference no lo hace.
static cambia el ciclo de vida de la variable sin modificar los derechos de acceso de la variable


¿Cuál es el resultado del siguiente código?¿Por qué?

#include<iostream>
using namespace std;
int& Add(int a, int b)
{
    
    
	int c = a + b;
	return c;
}
int main()
{
    
    
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1,2) is : " << ret << endl;
	return 0;
}

inserte la descripción de la imagen aquí
Nota: si la función vuelve fuera del alcance de la función, si el objeto devuelto no se ha devuelto al sistema, se puede devolver por referencia, y si se ha devuelto al sistema, se debe devolver por valor. (¡Explique que la devolución por referencia no es segura!)
inserte la descripción de la imagen aquí
Entonces, ¿cuál es el beneficio de usar la devolución por referencia?

Se puede crear una variable temporal menos para mejorar la eficiencia del programa (transferencia)
De hecho, hay otra función, que se discutirá más adelante (el retorno de muchas funciones de biblioteca también se devolverá por referencia)


Comparación de la eficiencia de paso por valor y paso por referencia

Con un valor como parámetro o tipo de valor de retorno, durante el paso del parámetro y el período de retorno, la función no pasará directamente el parámetro real ni devolverá la variable en sí misma directamente, sino que pasará el parámetro real o devolverá una copia temporal de la variable, por lo que el valor se utiliza como parámetro.O el tipo de valor de retorno, la eficiencia es muy baja, especialmente cuando el tipo de parámetro o valor de retorno es muy grande, la eficiencia es aún menor. Comparación
de rendimiento de valores y referencias como parámetros.

#include<iostream>
using namespace std;
#include <time.h>
struct A
{
    
    
	int a[10000];
};
void TestFunc1(A a)
{
    
    
}
void TestFunc2(A& a)
{
    
    
}
void TestRefAndValue()
{
    
    
	A a;
	//以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc1(a);
	size_t end1 = clock();

	//以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	//分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time : " << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time: " << end2 - begin2 << endl;
}
int main()
{
    
    
	TestRefAndValue();
	return 0;
}

inserte la descripción de la imagen aquí


Comparación de rendimiento de valor y referencia como tipos de valor de retorno

#include<iostream>
using namespace std;
#include <time.h>
struct A
{
    
    
	int a[10000];
};
A a;

A TestFunc1()
{
    
    
	return a;
}
A& TestFunc2()
{
    
    
	return a;
}
void TestRefAndValue()
{
    
    
	A a;
	//以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc1();
	size_t end1 = clock();

	//以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 1000000; ++i)
		TestFunc2();
	size_t end2 = clock();

	//分别计算两个函数运行结束后的时间
	cout << "TestFunc1()-time : " << end1 - begin1 << endl;
	cout << "TestFunc2()-time: " << end2 - begin2 << endl;
}
int main()
{
    
    
	TestRefAndValue();
	return 0;
}

inserte la descripción de la imagen aquí

A través de la comparación del código anterior, se encuentra que la eficiencia de pasar un valor y un puntero como parámetro y devolver un valor es muy diferente.


Diferencia entre referencia y puntero.

En términos de sintaxis, una referencia es un alias, que no tiene un espacio independiente y comparte el mismo espacio con su entidad de referencia.

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 10; int& ra = a;
	cout << "&a = " << &a << endl;
	cout << "&ra = " << &ra << endl;
	return 0;
}

inserte la descripción de la imagen aquí
De hecho, hay espacio en la implementación subyacente, porque las referencias se implementan como punteros.

#include<iostream>
using namespace std;
int main()
{
    
    
	int a = 10;
	int& b = a;
	int* p = &a;
	return 0;
}

Echemos un vistazo a la comparación del código ensamblador de referencias y punteros:
inserte la descripción de la imagen aquí
las referencias y los punteros son direcciones de almacenamiento

Diferencias entre referencias y punteros:

1. La referencia debe inicializarse cuando se define, y no se requiere el puntero
2. Después de que la referencia se refiere a una entidad en el momento de la inicialización, no puede referirse a otras entidades, y el puntero puede apuntar a cualquier entidad de el mismo tipo en cualquier momento
3. No hay una referencia NULL, pero hay 4. Los punteros NUL
tienen diferentes significados en sizeof: el resultado de la referencia es el tamaño del tipo de referencia, pero el puntero es siempre el número de bytes ocupados por el espacio de direcciones (4 bytes en plataformas de 32 bits)
5. La referencia se agrega automáticamente, es decir, la entidad a la que se hace referencia Aumenta en 1, el puntero se agrega automáticamente, es decir, el puntero se compensa hacia atrás por el tamaño de un tipo.
6. Hay varios niveles de punteros, pero no hay referencias de varios niveles
. 7. La forma de acceder a las entidades es diferente. El puntero debe ser desreferenciado explícitamente, y el compilador de referencia lo maneja solo.
8. Referencia Relativamente más seguro de usar que los punteros

Supongo que te gusta

Origin blog.csdn.net/QIYICat/article/details/119843659
Recomendado
Clasificación