<C++> C++11

Primeros pasos con C++11

C++11 es un estándar importante del lenguaje C++, lanzado en 2011. Introduce muchas características nuevas y mejoras destinadas a mejorar la eficiencia del desarrollo y la calidad del código.

Comparado con C++98/03, C++11 ha traído una cantidad considerable de cambios, incluidas unas 140 funciones nuevas y unas 600 correcciones de errores en el estándar C++03, lo que hace que C++11 se parezca más a un nuevo lenguaje concebido a partir de C++98/03. En comparación, C ++ 11 se puede usar mejor para el desarrollo de sistemas y bibliotecas, la sintaxis es más generalizada y simplificada, más estable y segura, no solo más poderosa, sino que también puede mejorar la eficiencia de desarrollo de los programadores.

1. Inicialización de lista uniforme

1.1 {} inicialización

En C++98, el estándar permite el uso de llaves {} para la inicialización de listas uniformes de elementos de matriz o estructura.

struct Point {
    
    
    int _x;
    int _y;
};

int main() {
    
    
    int array1[] = {
    
    1, 2, 3, 4, 5};
    int array2[5] = {
    
    0};
    Point p = {
    
    1, 2};
    Point p{
    
     1,2 };
    return 0;
}

C++11 amplía el ámbito de uso de la lista encerrada entre llaves (lista de inicialización), de modo que se puede usar para todos los tipos integrados y tipos definidos por el usuario. Al usar la lista de inicialización, puede agregar un igual signo (=), o No agregar . Aquí hay unos ejemplos:

struct Point {
    
    
    int _x;
    int _y;
};


int main() {
    
    
    int num1{
    
    10};            // 初始化整型变量
    double pi{
    
    3.14159};      // 初始化浮点型变量
    char ch{
    
    'A'};            // 初始化字符型变量
    bool flag{
    
    true};         // 初始化布尔型变量
    std::string name{
    
    "John"};// 初始化字符串对象

    int array1[]{
    
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int array2[5]{
    
    0};
    Point p{
    
    1, 2};


    std::vector<int> numbers{
    
    1, 2, 3, 4};// 初始化容器对象
    std::list<int> lt{
    
    1, 2, 3, 4};
    std::pair<int, double> p{
    
    42, 3.14};// 初始化pair对象

    //C++11中列表初始化也可以适用于new表达式中
    int *pa = new int[4]{
    
    0};
    return 0;
}

Al crear un objeto, también puede usar el método de inicialización de lista para llamar a la inicialización del constructor

class Date {
    
    
public:
    Date(int year, int month, int day)
        : _year(year), _month(month), _day(day) {
    
    
        cout << "Date(int year, int month, int day)" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    
    
    Date d1(2022, 1, 1);// old style()
    // C++11支持的列表初始化,这里会调用构造函数初始化
    Date d2{
    
    2022, 1, 2};//new style{}
    Date d3 = {
    
    2022, 1, 3};
    return 0;
}

Cuando se inicia la inicialización de la lista, si se produce un truncamiento de tipos, se informará una advertencia o un error

#include <iostream>

using namespace std;

int main() {
    
    


    short c = 65535;
    short d{
    
    65535};
    return 0;
}
//narrowing conversion of '65535' from 'int' to 'short int' inside { } [-Wnarrowing]

1.2 std::initializer_list

std::initializer_listes una plantilla de clase introducida en la biblioteca estándar de C++11 para simplificar el manejo de las listas de inicialización. Permite pasar un conjunto de valores en forma de lista de inicializadores en una función o constructor y acceder a estos valores de manera uniforme en el extremo receptor.

std::initializer_listProporciona una forma sencilla de acceder a los elementos de la lista de inicialización al sobrecargar el constructor y la interfaz del iterador. Se puede usar para definir parámetros de funciones, parámetros de constructores y otras clases de contenedores que aceptan listas de inicializadores.

¿Cuál es el tipo de std::initializer_list?

void test() {
    
    
    auto i1 = {
    
     10, 20, 30, 40, 50, 60, 70 };
    auto i2 = {
    
     10, 20, 30 };
    cout << typeid(i1).name() << endl;  //class std::initializer_list<int>
    cout << typeid(i2).name() << endl;  //class std::initializer_list<int>
}

Características y uso de std::initializer_list

  1. Funciones de constructor y miembro: std::initializer_list es una clase de plantilla que acepta elementos de cualquier tipo y proporciona constructores y funciones de miembro para manejar listas de inicialización. Estas funciones incluyen:

    • initializer_list(): El constructor predeterminado, que crea una lista de inicializadores vacía.

    • initializer_list(size_type count, const T& value): Constructor que crea una lista de inicialización que contiene el número especificado de elementos, cada uno de los cuales es un valor dado.

    • const T* begin() const: Devuelve un puntero al primer elemento de la lista de inicialización.

    • const T* end() const: Devuelve un puntero a la siguiente posición del último elemento en la lista de inicialización.

    • size_type size() const: Devuelve el número de elementos en la lista de inicialización.

  2. Uso de sintaxis: la sintaxis para usar std::initializer_listes similar al uso de llaves {}para inicializar una lista. Por ejemplo:

    std::initializer_list<int> myList = {
          
          1, 2, 3, 4, 5};
    
  3. Parámetros de función: se pueden std::initializer_listusar como parámetros de una función para pasar un conjunto de valores cuando se llama a la función. Por ejemplo:

    void myFunction(std::initializer_list<int> values) {
          
          
        // 使用初始化列表中的值
        for (const auto& value : values) {
          
          
            // 处理每个值
        }
    }
    
    // 调用函数
    myFunction({
          
          1, 2, 3, 4, 5});
    
  4. Parámetros del constructor: el constructor de una clase puede aceptar std::initializer_listparámetros para la inicialización utilizando una lista de inicialización cuando se crea el objeto. Por ejemplo:

    class MyClass {
          
          
    public:
        MyClass(std::initializer_list<int> values) {
          
          
            // 使用初始化列表中的值
            for (const auto& value : values) {
          
          
                // 处理每个值
            }
        }
    };
    
    // 创建对象时使用初始化列表
    MyClass obj = {
          
          1, 2, 3, 4, 5};
    

Escenarios de uso de std::initializer_list

std::initializer_listGeneralmente, se usa como un parámetro del constructor.C++ 11 agrega un std::initializer_listconstructor como parámetro a muchos contenedores en STL, por lo que es más conveniente inicializar el objeto contenedor. También se puede utilizar como parámetro de operator=, por lo que se puede asignar con llaves

void test() {
    
    
    auto i1 = {
    
     10, 20, 30, 40, 50, 60, 70 };
    auto i2 = {
    
     10, 20, 30 };

    initializer_list<int>::iterator it1 = i1.begin();
    initializer_list<int>::iterator it2 = i2.begin();
    cout << it1 << endl;   //00000053083EF1F8
    cout << it2 << endl;   //00000053083EF268
    
    initializer_list<int> i3 = {
    
     10,20,30,40,50,60,70,80 };
    initializer_list<int>::iterator it3 = i3.begin();
    cout << it3 << endl;   //00000053083EF308
}
void test2() {
    
    
	Date d1(2023,5,20);
	Date d2(2023,5,21);
	
	//initializer_list<Date>  从C++11开始常量数组会被识别成initializer_list
	vector<Date> v1 = {
    
     d1,d2 };   
	vector<Date> v2 = {
    
     Date(2023,5,20),Date(2023,5,21) };
	vector<Date> v3 = {
    
     {
    
    2023,5,20},{
    
    2023,5,21} };

	map<string, string> dict = {
    
     {
    
    "sort","排序"},{
    
    "string","字符串"},{
    
    "Date","日期"}};
	pair<string, string> kv1 = {
    
     "Date","日期" };
	pair<string, string> kv2{
    
     "Date","日期" };
}

std::initializer_listLa introducción de hace que trabajar con listas de inicializadores en C++ sea más conveniente y consistente. Su uso en parámetros de función y parámetros de constructor proporciona una forma concisa y flexible de manipular un conjunto de valores.

Deje que el vector simulado también admita {} inicialización y asignación

template<class T>
class vector {
    
    
public:
	typedef T* iterator;
	vector(initializer_list<T> l)
	{
    
    
		_start = new T[l.size()];
		_finish = _start + l.size();
		_endofstorage = _start + l.size();
		iterator vit = _start;
        //内嵌类型需要加上typename
		typename initializer_list<T>::iterator lit = l.begin();
		while (lit != l.end())
		{
    
    
			*vit++ = *lit++;
		}
		//for (auto e : l)
		//   *vit++ = e;
	}

	vector<T>& operator=(initializer_list<T> l) {
    
    
		vector<T> tmp(l);
		std::swap(_start, tmp._start);
		std::swap(_finish, tmp._finish);
		std::swap(_endofstorage, tmp._endofstorage);
		return *this;
	}
private:
	iterator _start;
	iterator _finish;
	iterator _endofstorage;
};

2. Declaración

2.1 automático

Antes de C++11 (igual que el lenguaje C), autoera un especificador de clase de almacenamiento que se usaba para indicar que la variable tiene una duración de almacenamiento automática. Al declarar una variable dentro de una función, si no se especifica explícitamente ningún especificador de clase de almacenamiento, el valor predeterminado es autola clase de almacenamiento.

autoLa clase de almacenamiento significa que las variables se crean automáticamente cuando ingresan a su alcance y se destruyen cuando salen de su alcance. autoEste es el comportamiento predeterminado de las variables en C, por lo que es poco común usar la palabra clave en C.

Pero la variable local definida en el campo local es el tipo de almacenamiento automático por defecto, por lo que automático tiene poco valor. En C++ 11, el uso original de auto se descarta y se usa para realizar la inferencia automática de tipos. Esto requiere una inicialización explícita, lo que permite que el compilador establezca el tipo del objeto definidor en el tipo del valor de inicialización.

Cuando usa la palabra clave auto, el compilador analiza la expresión de inicialización en el lado derecho del operador de asignación (=) y determina el tipo apropiado para la variable. Luego, se deduce el tipo y se asigna a la variable.

Aquí hay un ejemplo que ilustra el uso de auto:

void test(){
    
    
    auto x = 42;  // x 被推导为 int 类型
	auto y = 3.14;  // y 被推导为 double 类型
	auto z = "Hello, World!";  // z 被推导为 const char* 类型

	// 你也可以在更复杂的类型中使用 auto,比如迭代器:
	std::vector<int> numbers = {
    
    1, 2, 3, 4, 5};
	auto it = numbers.begin();  // it 被推导为 std::vector<int>::iterator 类型

	int i = 10;
	auto p = &i;
	auto pf = strcpy;

	cout << typeid(p).name() << endl;  //int * __ptr64
	cout << typeid(pf).name() << endl;  //char * __ptr64 (__cdecl*)(char * __ptr64,char const * __ptr64)
}

2.2 tipo de declaración

decltypeEs un mecanismo de deducción de tipo introducido por C++11 . Se utiliza para deducir el tipo de una variable o expresión a partir de una expresión. El tipo de una expresión se evalúa en tiempo de compilación y se utiliza como parte del tipo de retorno de una variable o función.

decltypeLa sintaxis para es la siguiente:

decltype(expression)

Entre ellos, expressionse encuentra una expresión, que puede ser un nombre de variable, llamada de función, conversión de tipo, etc.

decltypefunciona así:

  • Si expressiones un identificador (nombre de variable), decltype(expression)se deducirá el tipo del identificador.
  • Si expressiones una llamada de función, decltype(expression)se deducirá el tipo de retorno de la llamada de función.
  • Si expressiones una expresión, decltype(expression)se deducirá el tipo de expresión.

Aquí hay algunos ejemplos para ilustrar decltypeel uso de:

void test(){
    
    
    int x = 5;
	decltype(x) y = 10;  // 推导出 y 的类型为 int

	int foo();
	decltype(foo()) result = foo();  // 推导出 result 的类型为 foo() 函数的返回类型

	int a = 1;
	int b = 2;
	decltype(a + b) c = a + b;  // 推导出 c 的类型为 int,表达式 a + b 的类型为 int
}

decltypeUn uso común de es en la programación de plantillas, donde se puede usar para deducir un tipo a partir de un valor de retorno de función o expresión y usarlo como parámetro de plantilla o tipo de variable para otros tipos.

Cabe señalar que decltypela expresión no se ejecuta, solo se utiliza para deducir el tipo de expresión. Además, decltypelos tipos deducidos pueden conservar los calificadores de referencia y cv (const y volatile).

Algunos escenarios de uso de decltype

template<class T1, class T2>
void F(T1 t1, T2 t2) {
    
    
    decltype(t1 * t2) ret;
    cout << typeid(ret).name() << endl;   //int
}


int main() {
    
    
    const int x = 1;
    double y = 2.2;
    decltype(x * y) ret;// ret的类型是double
    decltype(&x) p;     // p的类型是int*
    cout << typeid(ret).name() << endl;   //double
    cout << typeid(p).name() << endl;     //int const * __ptr64
    F(1, 'a');
    
    // vector存储的类型跟x*y表达式返回值类型一致
	// decltype推导表达式类型,用这个类型实例化模板参数或者定义对象
	vector<decltype(x* y)> v;

    return 0;
}

2.3 puntos nulos

nullptres una constante de puntero nulo que se utiliza para representar un puntero nulo. Es una nueva característica introducida por el estándar C++ 11, que tiene como objetivo resolver algunos problemas de ambigüedad y ambigüedad de NULL en C++.

En C++, los punteros generalmente se usan para apuntar a objetos o funciones en la memoria. Mientras que un puntero nulo significa que el puntero no apunta a ningún objeto o función válidos. En versiones anteriores de C++, NULLlas macros solían usarse para representar punteros nulos, que normalmente se definían como 0 o (void*)0. Sin embargo, tal definición puede causar ambigüedad en algunos casos, ya que 0 puede usarse para representar el valor cero de tipos enteros.

Para resolver este problema, C++11 introdujo nullptrla palabra clave, que es una constante de puntero nulo explícito. Úselo nullptrpara indicar sin ambigüedades que un puntero es nulo sin confusión con el número entero 0. Esto evita algunos errores de programación y ambigüedades.

La sintaxis utilizada nullptres muy simple, simplemente asígnela directamente a la variable puntero, por ejemplo:

int* ptr = nullptr;  // 整型指针指向空

nullptrTambién se puede utilizar para operaciones de comparación con otros punteros. Cuando se compara con otros punteros, nullptrel resultado de será verdadero (es decir, el puntero es nulo). Por ejemplo:

int* ptr = nullptr;
if (ptr == nullptr) {
    
    
    // 指针为空
}

nullptrEs una constante de puntero nulo introducida por C++ 11, que se usa para representar claramente el puntero nulo sin causar ambigüedad. Proporciona una mejor seguridad de tipo y claridad de código, y es la representación recomendada de punteros nulos en la programación C++ moderna.

3. Rango para bucle

3.1 Sintaxis de rango para

En C++98, si desea recorrer una matriz, puede hacerlo de la siguiente manera:

void TestFor() {
    
    
    int array[] = {
    
    1, 2, 3, 4, 5};
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
        array[i] *= 2;
    for (int *p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)
        cout << *p << endl;
}

Para una colección de rangos , es redundante y, a veces, propenso a errores que el programador especifique el rango del ciclo. Por lo tanto, los bucles for basados ​​en rangos se introdujeron en C++11. Los paréntesis después del bucle for se dividen en dos partes por los dos puntos ":": la primera parte es la variable utilizada para la iteración en el rango y la segunda parte representa el rango que se va a iterar.

void TestFor() {
    
    
    int array[] = {
    
    1, 2, 3, 4, 5};
    for (auto &e: array)
        e *= 2;
    for (auto e: array)
        cout << e << " ";
}

Nota: De manera similar a los bucles ordinarios, puede usar continuar para finalizar este bucle o usar romper para saltar fuera del bucle completo.

3.2 Condiciones de uso del alcance de

1. El rango de iteraciones del bucle for debe ser definido

**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 ciclo for.

Nota: El siguiente código es problemático porque el alcance de for es incierto

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

2. El objeto iterado debe implementar las operaciones ++ y == .

4. Cambios en STL

C++11 presenta algunas mejoras importantes y nuevas funciones, así como algunos cambios y mejoras en la STL (Biblioteca de plantillas estándar). Los siguientes son los principales cambios en STL después de C++11:

  1. Move Semantics: C++11 introduce referencias Rvalue, constructores de Move y operadores de asignación de Move. Estas características permiten que los contenedores y algoritmos STL administren los recursos de manera más eficiente y brinden un mejor soporte para tipos móviles como std::unique_ptr y std::shared_ptr.
  2. Nuevos tipos de contenedores: C++11 presenta dos nuevos tipos de contenedores:
    • std::array: es un contenedor de matrices de tamaño fijo que proporciona una interfaz más segura y conveniente que las matrices de estilo C.
    • std::unordered_XXX: introduce contenedores hash como std::unordered_map y std::unordered_set para proporcionar operaciones rápidas de búsqueda e inserción basadas en hash.
  3. Nuevos algoritmos: C++11 introduce algunos algoritmos nuevos para enriquecer las funciones de STL:
    • std::move, std::move_backward: se usa para mover elementos.
    • std::copy_if, std::move_if: copia o mueve elementos según las condiciones.
    • std::find_if_not: Encuentra el primer elemento que no cumple la condición dada.
    • std::is_sorted, std::is_partitioned: se utiliza para comprobar si una secuencia está ordenada o dividida.
    • std::all_of, std::any_of, std::none_of: se utiliza para comprobar si los elementos de una secuencia cumplen una determinada condición.
  4. Expresiones lambda: C++11 introduce expresiones lambda, que permiten escribir funciones anónimas de una manera más compacta y conveniente en algoritmos STL. Usando expresiones lambda, puede escribir más fácilmente predicados personalizados (predicados) y otros objetos de función.
  5. Soporte de programación concurrente: C++11 introduce primitivas de programación concurrente como std::thread y std::mutex, lo que hace que STL sea más fácil de usar en un entorno de subprocesos múltiples. Además, se introducen tipos de operaciones atómicas como std::atomic y std::atomic_flag para implementar algoritmos sin bloqueo.
  6. Nuevos componentes de biblioteca estándar: C ++ 11 presenta algunos componentes de biblioteca estándar nuevos, como:
    • std::tuple: una tupla utilizada para representar un número fijo de valores heterogéneos.
    • std::chrono: proporciona operaciones y tipos de fecha y hora.
    • std::regex: una biblioteca que admite expresiones regulares.

Estos cambios y mejoras hacen que el STL posterior a C++ 11 sea más poderoso, más eficiente y brinda más herramientas y opciones para manejar las demandas de la programación C++ moderna.

Supongo que te gusta

Origin blog.csdn.net/ikun66666/article/details/131314726
Recomendado
Clasificación