Introducción a los conceptos básicos de C++ (Parte 2): auto, range for loop, palabra clave nullptr

Tabla de contenido

1. La palabra clave automática (C++11)

1.1 Introducción

1.2 Introducción

1.3 Reglas de uso

1.3.1 Auto combinado con punteros y referencias

1.3.2 Definir múltiples variables en la misma línea

1.3.3 Otras aplicaciones

1.3.4 C++11 automático

1.4 Escenarios en los que no se puede derivar auto

1.4.1 auto no se puede utilizar como parámetro de función

1.4.2. auto no se puede usar directamente para declarar una matriz

2. Bucle for basado en rango (C++11)

2.1 Sintaxis básica

2.2 Condiciones de uso

3. Valor nulo del puntero nullptr (C++11)

3.1 Puntero nulo en C++98

3.2 Precauciones


1. La palabra clave automática (C++11)

1.1 Introducción

Con la profundización del aprendizaje, el programa y los tipos utilizados se vuelven cada vez más complejos, por ejemplo: el tipo es difícil de deletrear/el significado no está claro, lo que facilita cometer errores.

p.ej:

#include <string>
#include <map>

int main() {
    std::map<std::string, std::string> m{
   
   {"apple", "苹果"},
                                         {"orange","橙子"},
                                         {"pear",  "梨"}};
    std::map<std::string, std::string>::iterator it = m.begin();
    while (it != m.end()) {
        //....
    }
    return 0;
}

std::map<std::string, std::string>::iterator es un tipo, pero el tipo es demasiado largo, es fácil escribirlo mal y es relativamente problemático de usar.

1.2 Introducción

El significado de auto en C/C++ temprano es: la variable modificada con auto es una variable local con memoria automática , pero desafortunadamente nadie la ha estado usando. ¿Puedes pensar por qué?

En C++11, el comité estándar le dio a auto un nuevo significado: auto ya no es un indicador de tipo de almacenamiento, sino un nuevo indicador de tipo para instruir al compilador, y las variables declaradas por auto deben ser compiladas por el compilador derivadas de periodo de tiempo.

#include <iostream>
#include <typeinfo>
using namespace std;

int TestAuto()
{
    return 10;
}

int main() {
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = TestAuto();
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl;
    //auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
    return 0;
}

En C++, typeides un operador que se utiliza para obtener la información de tipo de una expresión. Cuando lo llama , devuelve una cadena que representa el tipo de expresión. Esta cadena suele ser específica del compilador y es posible que no sea legible por humanos, pero se puede usar para la depuración u otros fines.typeid(d).name()d

Para usarlo typeid, debe incluir el archivo de encabezado <typeinfo>.

Recordatorio: cuando se usa auto para definir una variable, debe inicializarse. En la etapa de compilación, el compilador necesita deducir el tipo real de auto de acuerdo con la expresión de inicialización. Por lo tanto, auto no es una declaración de "tipo", sino un "marcador de posición" cuando se declara el tipo. El compilador reemplazará auto con el tipo real de la variable durante la compilación.

1.3 Reglas de uso

1.3.1 Auto combinado con punteros y referencias

Cuando usa auto para declarar un tipo de puntero, no hay diferencia entre usar auto y auto* , pero cuando usa auto para declarar un tipo de referencia, debe agregar &

int main() {
    int x = 10;
    auto a = &x;
    auto *b = &x;
    auto &c = x;
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    *a = 20;
    *b = 30;
    c = 40;
    return 0;
}

Aquí se deducirá que tanto a como b son de tipo int* , y c es de tipo int

1.3.2 Definir múltiples variables en la misma línea

Al declarar varias variables en la misma línea, las variables deben ser del mismo tipo, de lo contrario, el compilador informará un error.

Porque el compilador en realidad solo deduce el primer tipo y luego usa el tipo deducido para definir otras variables.

void TestAuto()
{
    auto a = 1, b = 2;
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

1.3.3 Otras aplicaciones

La ventaja más común de auto en la práctica es usarlo junto con el nuevo bucle for proporcionado por C++ 11, que se mencionará más adelante, y las expresiones lambda.

1.3.4 C++11 automático

Para evitar confusiones con auto en C++98, C++11 solo retiene el uso de auto como indicador de tipo.

1.4 Escenarios en los que no se puede derivar auto

1.4.1 auto no se puede utilizar como parámetro de función

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

El estándar de C++ estipula que los tipos de funciones de parámetros formales deben ser tipos explícitamente especificados, en lugar de utilizar la deducción de tipos. Por lo tanto, el uso de código como este autocomo un tipo de parámetro de función no se podrá compilar.

1.4.2. auto no se puede usar directamente para declarar una matriz

void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
}

En C++, autocuando se usa para declarar una variable, el compilador deducirá el tipo de la variable según el tipo de la expresión de inicialización.

Sin embargo, auto b[] = {4, 5, 6};tal sintaxis no está permitida.

Para este caso, el compilador no puede deducir bel tipo de arreglo porque el inicializador {4, 5, 6}es una lista de inicializadores, no un tipo de arreglo concreto. Cuando se usa auto, el compilador necesita conocer el tipo de la variable explícitamente para asignar correctamente la memoria y realizar la verificación de tipo.

2. Bucle for basado en rango (C++11)

C++11 presenta una nueva sintaxis de bucle for llamada bucle for basado en rango. Le permite iterar sobre los elementos en el contenedor de manera más conveniente, sin usar iteradores o índices. Este ciclo funciona para todos los contenedores que admiten iteradores, como matrices, vectores, listas, conjuntos, etc.

2.1 Sintaxis básica

for (element_type variable : container) {
    // 循环体,使用 variable 访问容器中的元素
}

donde element_typees el tipo de elemento en el contenedor, variablees el nombre de la variable que usa en el ciclo y containeres el contenedor a recorrer.

Como ejemplo, usamos un bucle for basado en rango para iterar sobre un vector:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 使用基于范围的for循环遍历向量
    for (int num : numbers) {
        std::cout << num << " ";
    }

    return 0;
}

En este ejemplo, declaramos un numbersvector llamado , luego usamos un bucle for basado en rango para iterar sobre los elementos en ese vector e imprimir cada elemento en la salida estándar.

El bucle for basado en rango tiene grandes ventajas en términos de legibilidad y concisión del código, especialmente para atravesar todos los elementos del contenedor. Sin embargo, si necesita acceder a un índice o iterador dentro del cuerpo del ciclo, aún necesita usar un ciclo for tradicional.

Vea aquí porque es posible que el lector no haya tocado el contenedor, el autor usa el ejemplo de matriz nuevamente:

#include <iostream>

int main() {
    int arr[] = {1, 2, 3, 4, 5};

    // 使用基于范围的for循环遍历数组
    for (int num : arr) {
        std::cout << num << " ";
    }

    return 0;
}

La salida será:1 2 3 4 5

En el ejemplo anterior, declaramos una arrmatriz con nombre y usamos un bucle for basado en rango para iterar sobre los elementos de esa matriz e imprimimos cada elemento en la salida estándar.

Debe tenerse en cuenta que cuando se usa un bucle for basado en rango para atravesar una matriz, el tipo de la variable de bucle debe coincidir con el tipo del elemento de la matriz; de lo contrario, se producirá un error de compilación.

2.2 Condiciones de uso

El rango de iteraciones del bucle for debe ser determinista

Para una matriz, es el rango del primer elemento y el último elemento de la matriz; para una clase, se deben proporcionar los métodos de comienzo y fin, y comienzo y fin son el rango de las iteraciones del bucle for.

Ejemplo:

void TestFor(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}

 Hay un problema con el código anterior, porque después de pasar la matriz, la matriz ya no es una matriz, sino un puntero, lo que hace que el rango sea incierto.

3. Valor nulo del puntero nullptr (C++11)

3.1 Puntero nulo en C++98

En una buena práctica de programación C/C++, es mejor dar a la variable un valor inicial adecuado al declarar una variable, de lo contrario, pueden ocurrir errores inesperados, como punteros no inicializados . Si un puntero no tiene un punto legal, básicamente lo inicializamos de la siguiente manera:

void TestPtr()
{
    int* p1 = NULL;
    int* p2 = 0;
// ……
}

NULL es en realidad una macro En el archivo de encabezado C tradicional (stddef.h), puede ver el siguiente código:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

Como puede ver, NULL puede definirse como la constante literal 0, o como una constante de un puntero sin tipo (void*). No importa qué tipo de definición se adopte, inevitablemente se encontrarán algunos problemas al usar punteros de valores nulos, como:

void f(int) {
    cout << "f(int)" << endl;
}

void f(int *) {
    cout << "f(int*)" << endl;
}

int main() {
    f(0);
    f(NULL);
    f((int *) NULL);
    return 0;
}

La intención original del programa es llamar a la versión de puntero de la función f(int*) a través de f(NULL), pero dado que NULL se define como 0, es contrario a la intención original del programa.

En C++98, la constante literal 0 puede ser un número entero o una constante de puntero sin tipo (void*), pero el compilador la trata como una constante entera de manera predeterminada. (vacío *)0 .

3.2 Precauciones

  1. 1. Cuando se usa nullptr para representar el valor nulo del puntero, no es necesario incluir el archivo de encabezado, porque nullptr se introduce como una nueva palabra clave en C++11.
  2. 2. En C++ 11, sizeof(nullptr) y sizeof((void*)0) ocupan la misma cantidad de bytes
  3. 3. Para mejorar la solidez del código, se recomienda utilizar nullptr al representar el valor nulo del puntero.

¡Hasta ahora, la introducción a C++ ha terminado! !

Supongo que te gusta

Origin blog.csdn.net/weixin_57082854/article/details/132089384
Recomendado
Clasificación