Conceptos básicos de C ++: sobrecarga de funciones, referencia, función en línea, automático, rango para bucle


Función de sobrecarga

C ++ introdujo una nueva característica, sobrecarga de funciones.

Bajo el mismo alcance, para el mismo nombre de función, los parámetros de la función son diferentes, el orden de los diferentes tipos de parámetros es diferente y el número de parámetros es diferente, todos pueden formar una sobrecarga de la función (diferentes nombres de parámetros, diferentes valores de retorno no forman sobrecargas) )

La sobrecarga de funciones se usa principalmente para manejar datos con la misma función pero con diferentes tipos.

Por ejemplo

int test(int i, int j)
{
	cout << "test" << endl;
}

int test(double i, int j)
{
	cout << "test" << endl;
}

int test(double i, int j, int k)
{
	cout << "test" << endl;
}

¿Por qué C ++ admite sobrecarga y el lenguaje C no?

Debido a que el procesamiento de Windows es más complicado, es más intuitivo usar gcc y g ++ en Linux.

En primer lugar, debemos saber que cuando el enlazador ve que se llama a una función, irá a la tabla de símbolos para encontrar el nombre de la función correspondiente, obtener la dirección de la función y luego enlazar

Primero observe cómo se procesa el lenguaje C.
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
A través del desmontaje, podemos ver que el lenguaje C no procesa el nombre de la función. Es decir, no importa cuántos parámetros tengamos, los tipos de parámetros y el orden de los parámetros, solo reconocemos la función. Nombre, si hay una segunda con el mismo nombre de función, se considera redefinición.

Miremos de nuevo C ++.
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Aquí puede ver que C ++ ha procesado el nombre de la función. La función comienza con _Z4, luego el nombre de la función y finalmente la abreviatura de todos los parámetros.
_Z es el prefijo de todas las funciones, 4 es el número de caracteres en el nombre de la función, por ejemplo, el primer _Z4testii representa la prueba del nombre de la función, con cuatro caracteres, los parámetros son ii.

Esta es la razón por la cual el valor de retorno es diferente y el nombre del parámetro no constituye una sobrecarga. C ++ usa esta regla de modificación del nombre de la función para implementar la sobrecarga de la función.

"C" externa

A veces estamos usando C ++. Para algunas funciones que desean compilarse en estilo C, simplemente agregue "C" externa antes de la función, lo que significa decirle al compilador que compile la función de acuerdo con las reglas del lenguaje C.

Inserte la descripción de la imagen aquí


Cita

Conceptos citados

La referencia es dar otro nombre a un objeto. Puede usar este alias para operar en el objeto original. Al mismo tiempo, el compilador no abrirá espacio para la variable de referencia. Comparte un espacio con el objeto al que se refiere.

Uso: tipo y nombre del objeto de referencia = entidad de referencia

int main()
{
	int i = 5;
	int& j = i;
	cout << i << ' ' << j << endl;
	j = 8;
	cout << i << ' ' << j << endl;

	return 0;
}

Inserte la descripción de la imagen aquí

Funciones referenciadas

  1. La referencia debe inicializarse cuando se define (dado que la referencia es un alias para un objeto, debe inicializarse)
  2. Un objeto puede tener múltiples referencias
  3. Una vez que hace referencia a una entidad, ya no puede hacer referencia a otras entidades (un poco como la constante de nivel superior de un puntero)

A menudo citado

int main ()

{
	const int i = 5;
	int& j = i;
	//错误的
	const int & k = i;
	正确的
}

Aquí se hace referencia a la constante i, porque i es una constante, por lo que su valor no puede modificarse, por lo que cuando usamos una referencia normal, ampliaremos sus permisos, lo que da como resultado la capacidad de modificar i a j, esto es Irrazonable, por lo que el compilador informará un error. Solo use referencias constantes

int main()
{
	int x = 6;
	const int & y = x;
}

Al mismo tiempo, usamos una referencia constante para referirnos a esta x, x se puede modificar ey no se puede modificar, de modo que los permisos se reducen y se vuelven de solo lectura, por lo que esto es factible.

Referencia cruzada

int main()
{
	double x = 3.14;
	int& y = x;
	//错误的
	
	const int& z = x;
	//正确的
	return 0;
}

Aquí usamos y y z para referirnos a x, pero el compilador indicará que y informa un error, z es legal y también es de tipo cruzado. ¿Por qué es este el caso?

Porque cuando el tipo es diferente, se usará una cantidad temporal para referirse a x, y luego se hará referencia a la cantidad temporal.

int main()
{
	double x = 3.14;
	int& y = x;
	/*
		等价于
		const int &temp = x;
		int &y = temp;
	*/

	const int& y = x;
	/*
		等价于
		const int &temp = x;
		const int &z = temp;
	*/

	return 0;
}

Si se trata de una referencia ordinaria, en realidad nos estamos refiriendo a temp, pero queremos modificar x, pero esto no es posible porque la cantidad temporal es constante, por lo que este comportamiento es ilegal.

Si es una referencia constante, significa que no modificaremos x, por lo que incluso si la referencia es en realidad una cantidad temporal temporal, este comportamiento es legal

Escenarios de uso citados

  1. Como parámetro
struct A
{
	int arr[1000000];
};

void test(A& s1)
{

}

Supongamos que tenemos una estructura súper grande. Si pasamos la estructura directamente, se generará una variable temporal para copiar la estructura en los parámetros formales. Esta es una gran sobrecarga, pero si usamos referencias, Solo se pasa un alias, pero debe tenerse en cuenta lo mismo que antes: debido a que la cantidad temporal es constante, si queremos pasar una constante, debemos agregar constante antes de la referencia.

  1. Como valor de retorno
> int& Add(int a, int b) 
{
	int c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << ret << endl;
	return 0;
}

Para tal código, primero podemos pensar que ret será 3
Inserte la descripción de la imagen aquí
pero en realidad es 7.

Debido a que devolvemos una referencia a c, pero c solo existe en el marco de la pila en el momento de la llamada. Una vez completada la llamada, el marco de la pila se destruirá. Aunque los datos no se borrarán después de la destrucción, sino el acceso al área El permiso será liberado, puede ser utilizado por la siguiente función de llamada o puede ser utilizado por una operación, por lo que este es un comportamiento extremadamente inseguro. Si queremos devolver una referencia, debemos Asegúrese de que el objeto seguirá existiendo en el alcance de la función.
El 7 anterior es el valor de c modificado después de la segunda llamada.

Por lo tanto, si necesita cotizar como valor de retorno, debe asegurarse de que el alcance de la función esté fuera y que el objeto devuelto no se devuelva al sistema y aún exista.

Cuando se utiliza un valor como parámetro o valor de retorno, se pasa o se devuelve una copia temporal de la variable original al pasar parámetros y regresar. Esta eficiencia es muy baja, especialmente cuando los datos son particularmente grandes, pero si usa una referencia como Con los parámetros, no habrá tales problemas.

Referencias y punteros

Anteriormente dijimos que una referencia es un alias para un objeto. No hay un espacio separado, y comparte un espacio con la entidad a la que se refiere, pero esto es solo un concepto gramatical . Al mismo tiempo, descubrimos que la referencia es realmente similar al puntero, es más como un puntero constante de nivel superior, por lo que podemos ingresar al desmontaje para ver si hay una relación entre ellos.

int main()
{
	int x = 5;
	int& y = x;
	int* z = &x;

	return 0;
}

Inserte la descripción de la imagen aquí
Bajo desmontaje, podemos ver que la implementación de punteros y referencias en el ensamblaje es exactamente la misma.

Por lo tanto, podemos llegar a la conclusión de que las referencias se implementan de acuerdo con los punteros, y las nuevas funciones se encapsulan en función de los punteros

Diferencias entre referencias y punteros:

  1. Las referencias deben inicializarse cuando se definen, no se requieren punteros
  2. Después de que una referencia se refiere a una entidad durante la inicialización, ya no puede referirse a otras entidades, y el puntero puede apuntar a cualquier entidad del mismo tipo en cualquier momento
  3. Sin referencia NULL, pero puntero NULL
  4. El significado es diferente en sizeof: el resultado de referencia es el tamaño del tipo de referencia, pero el puntero siempre es el número de bytes ocupados por el espacio de direcciones (4 bytes en plataformas de 32 bits)
  5. La autoadición referenciada se refiere a la entidad referenciada incrementada en 1, y la autoadición del puntero se refiere al desplazamiento del puntero por un tamaño de letra
  6. Hay punteros multinivel, pero no hay referencias multinivel
  7. La forma de acceder a la entidad es diferente, el puntero debe ser desreferenciado explícitamente y el compilador de referencia lo maneja
  8. Las referencias son relativamente más seguras de usar que los punteros

Función en línea

Con línea es una función en línea, el compilador de código funcionará en lugar de llamar a una función de palabras clave de expansión de función en línea modificada en tiempo de compilación, menos la función de costo de empuje, programas de mejora de la eficiencia que se ejecutan
Inserte la descripción de la imagen aquí
ejemplo de un simple tal Código
Si lo llamamos directamente
Inserte la descripción de la imagen aquí
en el ensamblaje, podemos ver que creará un nuevo marco de pila, empujará los parámetros 3 y 4 a la pila y luego calculará y devolverá el resultado
. Si agrega en línea delante de la función para hacerla en línea Cuando
Inserte la descripción de la imagen aquí
observe la función en este momento, encontrará que expande directamente el código de la función directamente en la llamada y no crea un nuevo marco de pila.

Características de las funciones en línea.

  • Las funciones en línea son un método para intercambiar espacio por tiempo, lo que ahorra la sobrecarga de crear marcos de pila y empujar pilas. Sin embargo, el código es muy complicado y las funciones con bucles o recursión no son adecuadas como funciones en línea, incluso si se declaran como El compilador de funciones en línea también lo ignorará automáticamente.
  • Las funciones en línea no se pueden declarar y definir por separado, porque una vez que se declaran como funciones en línea, se expandirán directamente cuando se las llame. Sin la dirección de la función, no se puede vincular a la parte definida.

Vale la pena mencionar que las funciones en línea son algo similares a las funciones macro en C. Aunque el rendimiento de las macros es bueno, porque las macros carecen de controles de seguridad de tipo y no se pueden depurar (el reemplazo de macro se realizó en la etapa de preprocesamiento), en C ++ Las funciones de macro medias se reemplazan por funciones en línea , y las definiciones de macro constante se reemplazan por const.


auto

Cuando programamos, a menudo necesitamos asignar el valor de la expresión a una variable, pero a veces no sabemos cuál es el tipo de expresión. Para resolver este problema, C ++ 11 introdujo el especificador de tipo automático, que se puede usar para hacer el compilador En lugar de analizar el tipo de expresión .

Como auto necesita dejar que el compilador infiera el tipo de la variable a través del valor inicial, entonces auto debe tener un valor inicial.

int main()
{
	auto i = 2.7;
	cout << i <<"的类型为:" << typeid(i).name() << endl;
}

Inserte la descripción de la imagen aquí

Reglas automáticas

  1. auto se puede combinar con punteros y referencias
int main()
{
	int i = 4;
	auto a1 = &i;
	auto *a2 = &i;
	auto& a3 = i;

	cout <<"a1的类型为:" << typeid(a1).name() << endl;
	cout << "a2的类型为:" << typeid(a2).name() << endl;
	cout << "a3的类型为:" << typeid(a3).name() << endl;
}

Inserte la descripción de la imagen aquí
Como auto puede reconocer el tipo de puntero, se puede agregar sin *, pero la referencia se debe agregar con &, ya que C ++ no se refiere a este tipo, la referencia es solo un alias modificado, por lo que su esencia sigue siendo int.

  1. Definir múltiples variables en la misma línea.
int main()
{
	auto i = 1, j = 2; //正确,同一行都是相同类型
	auto x = 3, y = 4.8;//错误,同一行类型不同
}
  1. Auto no se puede usar como parámetro de una función.
    Inserte la descripción de la imagen aquí
    Auto no se puede usar como tipo de parámetro formal. El compilador no puede deducir los tipos de i y j.

  2. auto no se puede usar directamente para declarar una matriz
    Inserte la descripción de la imagen aquí

  3. auto generalmente ignora la constante de nivel superior

int main()
{
	const int i = 5;
	//i : const int 
	auto a1 = i;
	//忽略了顶层const ,al : int
	const auto a2 = i;
	//加上const, a2 : const int
}

Rango para bucle

Para algunas colecciones con rangos, es posible que no conozcamos su longitud o crucemos accidentalmente el límite cuando usamos bucles. Para resolver este problema, c ++ 11 introdujo un rango para bucle.

El bucle se divide en dos partes, la primera parte es el tipo de la variable y la segunda parte es el alcance de la iteración, separadas por: en el medio.

int main()
{
	vector<int> vec{ 1, 3, 5, 7, 9 };

	for (auto i : vec)
	{
		cout << i << ends;
	}

}

Inserte la descripción de la imagen aquí
Debe tenerse en cuenta que i es una variable temporal para copiar los datos originales. Si desea modificar los datos originales, debe cambiarlos a & i (ambas referencias a los datos originales)


Publicado 60 artículos originales · elogiado 78 · vistas 6321

Supongo que te gusta

Origin blog.csdn.net/qq_35423154/article/details/105435404
Recomendado
Clasificación