C++: conceptos básicos

Cuando aprendió C++ por primera vez, ¿alguna vez pensó por qué C++ admite la sobrecarga pero C no? ?

De hecho, un programa debe seguir cuatro pasos para ejecutarse.

  1. preprocesamiento
  2. compilar
  3. Compilacion
  4. Enlace

La fase de preprocesamiento pasará por descomentación, reemplazo de macros, expansión del archivo de encabezado, compilación condicional... 

La fase de compilación generará código ensamblador, que pasará por análisis de sintaxis, análisis léxico, análisis semántico, resumen de símbolos... (Para más detalles, puedes leer "Autocultivo del programador", que se explicará en detalle en su segundo capítulo)


La fase de ensamblaje generará un binario a partir del ensamblaje y luego generará una tabla de símbolos en esta etapa.

La fase de enlace fusiona tablas de segmentos, resume tablas de símbolos y redirecciona.

 

  1. En un proyecto real, generalmente consta de múltiples archivos de encabezado y archivos fuente, que luego se compilan y vinculan para finalmente formar un archivo .o. Si nuestro test.cpp llama a la función agregar en add.cpp, después de la compilación y antes de vincular, No hay dirección de la función agregar en el archivo de destino de o, porque la función agregar está en add.cpp, por lo que la función agregar está en add.o. Entonces, ¿qué debemos hacer? ?
  2. La fase de enlace resuelve este problema. Cuando el enlazador ve que test.o llama a la función agregar en add.o, pero no hay una dirección de agregar, irá a la tabla de símbolos en add.o para encontrar la dirección de agregar, y luego enlace a Together.
  3. En este momento, el vinculador lo encontrará a través de las reglas de modificación del nombre de la función, y las reglas de modificación del nombre de la función de diferentes compiladores son diferentes.

Aquí usamos gcc y g++ en Linux como ejemplo:
3f0086c4a2404671932a4a7a719de5cc.png

Ahora volvamos a nuestra pregunta original, ¿por qué C++ admite la sobrecarga? ? ? Esto se debe a que C ++ se puede distinguir por las reglas de modificación del nombre de la función, y después de las reglas de modificación del nombre de la función C, los nombres de las funciones son los mismos, por lo que no se pueden sobrecargar.

 Entonces surge una pregunta: si hay dos funciones con el mismo nombre de función y los mismos parámetros, pero diferentes valores de retorno, ¿puede esto constituir una sobrecarga? ?
Por supuesto que no, porque el compilador no puede determinar si constituye una sobrecarga utilizando diferentes valores de retorno.


Referencia: una referencia no define una variable, pero le da un alias a una variable existente. El compilador no abrirá espacio adicional, pero compartirá el mismo espacio con la variable.

Las referencias a menudo se comparan con punteros y las referencias se utilizan a menudo como valores de retorno. En este caso, utilizar referencias como valores de retorno puede mejorar la eficiencia.

#include <iostream>
#include <time.h>
using namespace std;

struct A
{
    int a[10000];
};

A a;

// 值返回
A TestFunc1()
{
    return a;
}

// 引用返回
A &TestFunc2()
{
    return a;
}

void TestReturnByRefOrValue()
{
    // 以值作为函数的返回值类型
    size_t begin1 = clock();
    for (size_t i = 0; i < 100000; ++i)

        TestFunc1();
    size_t end1 = clock();

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

    // 计算两个函数运算完成之后的时间
    cout << "TestFunc1 time:" << end1 - begin1 << endl;
    cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

int main()
{
    TestReturnByRefOrValue();

    return 0;
}

5c12eba394a94fd2a22c3feb7a1e8633.png

Pero hay una diferencia muy importante:

  1. Desde un nivel gramatical, una referencia es un alias, que no tiene espacio independiente y comparte el mismo espacio con la entidad a la que hace referencia.
  2. En el nivel inferior, el nivel inferior de referencias se implementa mediante punteros.

889ac0089f194666a396b2bd1a50455f.png

 Las 6 funciones miembro predeterminadas de la clase:

9069dfe6e9a445e88423a41ceade5050.png

 

Constructor: El constructor es una función miembro especial, cabe señalar que el trabajo del constructor es inicializar el objeto.

Hay tres constructores predeterminados:

  1. Cuando el usuario no muestra la implementación, el compilador genera automáticamente
  2. El usuario muestra la implementación, pero sin ningún parámetro (constructor sin argumentos)
  3. Implementación de visualización del usuario, todos los parámetros tienen valores predeterminados (constructor predeterminado completo)

ps: estos tres constructores predeterminados no pueden existir al mismo tiempo. Si 2 3 existen al mismo tiempo, entonces cuando se llame a una clase de espacio-tiempo, habrá ambigüedad al llamar.

Las características son las siguientes:

  1. El nombre de la función es el mismo que el nombre de la clase.
  2. sin valor de retorno
  3. El constructor se llama automáticamente cuando se crea una instancia del objeto.
  4. Los constructores pueden estar sobrecargados.

Al crear un objeto, el compilador llamará automáticamente al constructor y asignará un valor inicial a cada miembro. En este momento, cada miembro revisará la lista de inicialización.

Lista de inicialización: comienza con dos puntos, seguidos de una lista de miembros de datos separados por comas, cada "variable miembro" seguida de un valor o expresión inicial entre paréntesis.

class A
{
    A()
    :_a(a)
    ,_b(b)
    {}

    private:
    int _a;
    int _b;
};

Por supuesto, cada miembro solo puede inicializarse una vez durante la inicialización, pero hay tres tipos que deben colocarse en la lista de inicialización.

  1. variable miembro constante
  2. Variables miembro de referencia
  3.  Miembros de tipo personalizado (y la clase no tiene un constructor predeterminado)

Destructor: a diferencia del constructor, el destructor llama automáticamente al destructor cuando se destruye el objeto para completar la limpieza de los recursos en el objeto.

Las características son las siguientes:

  1. El destructor debe estar delante del nombre de la clase + ~
  2. Sin parámetros, sin valor de retorno
  3. Una función tiene solo un destructor y es mejor convertirla en una función virtual, lo que constituye una condición para el polimorfismo.

Constructor de copia: solo hay un parámetro, que es una referencia a este tipo. Cuando se inicializa un objeto existente para crear un nuevo objeto, se llamará automáticamente al constructor de copia.

Las características son las siguientes:

  1. El constructor de copia es una sobrecarga del constructor.
  2. Los parámetros del constructor de copia deben ser una referencia de este tipo. Si no se agrega ninguna referencia, el constructor de copia se llamará infinitamente recursivamente, lo que eventualmente provocará un desbordamiento de pila.

bec542f2436345f09d0fe5000025e67c.png

 

El constructor de copia generado por el compilador de forma predeterminada es una copia de valor y no se puede utilizar en algunos escenarios, por lo que debemos realizar una copia profunda.

C++ 11 agrega dos funciones miembro predeterminadas:

  1. mover constructor
  2. Mover sobrecarga del operador de asignación

Existen requisitos específicos para escribir sobrecargas de constructores de movimientos y operadores de asignación de movimientos:

  • Si no implementa el constructor de movimiento usted mismo y no implementa el destructor, copie la construcción o copie la sobrecarga de asignaciones. Luego, el compilador generará automáticamente un constructor de movimientos predeterminado. El constructor de movimientos generado de forma predeterminada realizará una copia miembro por byte para los miembros de tipo integrado. Para los miembros de tipo personalizado, debe ver si el miembro implementa el constructor de movimientos. Si es así, llame al constructor de movimientos. Si no, llame al predeterminado Construcción móvil.
  • Si no ha implementado la función de sobrecarga de asignación de movimiento usted mismo y no ha implementado la sobrecarga de destructor, copia de construcción o copia de asignación
    , el compilador generará automáticamente una asignación de movimiento predeterminada. El constructor de movimientos generado por defecto, para dentro
    La configuración de los miembros de tipo realizará la copia miembro por miembro, byte por byte. Para personalizar los miembros de tipo, debe ver si el miembro implementa la asignación de movimiento.
    Valor, si está implementado, llame a la asignación de movimiento, si no está implementado, llame a la asignación de copia. (La asignación de movimiento predeterminada es la misma que la del constructor de movimientos anterior.
    Exactamente similar)

 Sobrecarga del operador de asignación: C++ introduce la sobrecarga del operador para mejorar la legibilidad del código. La sobrecarga del operador es una función con un nombre de función especial. También tiene un tipo de valor de retorno, un nombre de función y una lista de parámetros. Su tipo de valor de retorno es el mismo que un tipo de función ordinaria.

Las características son las siguientes:

  1. Los operadores sobrecargados deben tener un parámetro de tipo de clase
  2. Como función miembro de una clase, el parámetro formal es 1 menos que el operando, porque el primer parámetro oculto es el puntero this.
  3. .* tamaño de:? .  No se puede cargar

6f31b195b37f437aa7d3c750075fb59a.png

Echemos un vistazo a la sobrecarga del operador de asignación de cadenas: no es difícil encontrar que su valor de retorno es string&.

Piense por qué el valor de retorno es una referencia. ?

  • Si el valor de retorno es una referencia, puede mejorar la eficiencia del retorno, porque habrá una construcción de copia menos. El valor de retorno es para admitir la asignación continua.

El parámetro es constante T& porque puede admitir valores l o valores r, y pasar referencias también mejora la eficiencia.


palabra clave explícita:

El constructor no solo puede construir sino también inicializar objetos. Para un solo parámetro o un constructor con valores predeterminados excepto el primer parámetro, también tiene la función de conversión de tipo implícita. La función de la palabra clave explícita es modificar el constructor y prohibir la conversión de tipos implícita por parte del constructor.


miembros estáticos:

Las variables miembro modificadas con estática se denominan variables miembro estáticas y las funciones miembro modificadas con estática se denominan funciones miembro estáticas. Las variables miembro estáticas deben inicializarse fuera de la clase.

class A
{

    static int _a;
};

int A::_a = 1;

 

Las características son las siguientes:

  1.  Los miembros estáticos son compartidos por todos los objetos de clase y existen en el área estática.
  2. Las variables miembro estáticas deben inicializarse fuera de la clase, no es necesario agregar estática al definirlas, simplemente se declaran en la clase.
  3. Se pueden pasar miembros estáticos de claseNombre de clase::miembro estáticooObjeto::miembro estático para visitar
  4. Las funciones miembro estáticas no tienen estos punteros y no pueden acceder a ninguna variable miembro no estática.
  5. Los miembros estáticos también se ven afectados por modificadores de clase y restringidos por calificadores de acceso público, de protección y privado.

 

 

 

Supongo que te gusta

Origin blog.csdn.net/m0_72165281/article/details/134257889
Recomendado
Clasificación