std::cualquier clase en C++

any es una de las características más nuevas de C++17 que proporciona un contenedor seguro para almacenar valores únicos de cualquier tipo. En términos sencillos, es un contenedor que permite almacenar cualquier valor en él sin preocuparse por la seguridad del tipo . Actúa como una extensión de C++ al imitar un comportamiento similar a un tipo de objeto en .NET/Java o un tipo void* en lenguaje C. Ha sido diseñado en base a boost::any y está disponible en el archivo de encabezado «cualquiera» .

Sintaxis:

any var= value/object; 

donde el valor es algo así como «17» o «Hola Mundo»

Inicialización de cualquier:

any se puede construir de tres maneras diferentes usando:

  1. Copiar inicialización

    Sintaxis:

    any variable_name = object/value;
    
  2. Constructor parametrizado / inicializador de llaves.

    Sintaxis:

    any variable_name ( object/value);
    
  3. Usando el operador de asignación

    Sintaxis:

    any variable_name;
    variable_name= object/value;
    
  4. Convirtiendo el valor any_var en su tipo original:

    Se debe usar any_cast<type>( any_var ) para convertir el valor de any_var en su tipo original . Si un valor almacenado tiene un tipo diferente al que intenta convertir, el compilador lanzará una excepción » bad_any_cast «.

    Nota: El tipo durante el lanzamiento debe ser exactamente el mismo que el tipo original. No hay promoción o degradación automática durante el reparto. Por lo tanto, se debe tener especial cuidado al convertir el valor a su tipo original.

    Un ejemplo simple (ilustra la construcción/valores de lectura de cualquiera)

    #include <any>
    #include <iostream>
    #include <string>
    using namespace std;
      
    int main()
    {
        try {
      
            // Integer 42:  Using the copy initialisation
            any value = 42;
            cout << "\n Value: "
                 << any_cast<int>(value);
      
            // Using the assignment operator
            // to store a string
            value = "Hello World";
            cout << "\n Value: "
                 << any_cast<const char*>(value);
      
            // Using the parametrized constructor
            any val(19.0);
            cout << " \n Value: "
                 << any_cast<double>(val);
      
            any val_brace{ string("Brace Initialisation") };
            cout << " \n Value: "
                 << any_cast<string>(val_brace);
        }
      
        catch (bad_any_cast& e) {
            cout << "\n"
                 << e.what();
        }
        return 0;
    }

    Producción:

     Value: 42 
     Value: Hello World 
     Value: 19 
     Value: Brace Initialisation
    

    Funciones de los miembros:

    1. emplace : Cambia el objeto contenido, construyendo el nuevo objeto directamente
    2. reset : Destruye el objeto contenido (Llama al destructor del objeto)
    3. has_value : comprueba si «cualquier_variable» tiene un valor dentro de ella
    4. type : Devuelve el id de tipo del valor contenido

    Veamos los métodos uno por uno en detalle:

  • emplace: la función de miembro emplace es similar al operador de asignación y se usa para cambiar el objeto contenido con un nuevo objeto.

    Programa:

    // C++ program to demonstrate
    // emplace() method of any class
      
    #include <any>
    #include <iostream>
    #include <string>
      
    int main()
    {
        try {
            any value = 4.2;
            cout << " \n Value:  "
                 << any_cast<double>(value);
      
            value.emplace<int>(44);
            cout << " \n Value:  "
                 << any_cast<int>(value);
        }
        catch (bad_any_cast& e) {
            cout << "\n"
                 << e.what();
        }
        return 0;
    }

    Producción:

    Value:  4.2 
    Value:  44
    
  • reset: Destruye el objeto contenido llamando al destructor del objeto contenido.

    Programa:

    // C++ program to demonstrate
    // reset() method of any class
      
    #include <any>
    #include <iostream>
    #include <string>
      
    int main()
    {
        try {
            any var = 4.2;
            cout << " \n Value:  "
                 << any_cast<double>(var);
      
            var.reset();
            if (!var.has_value())
                cout << " \n No value found in var variable";
        }
        catch (bad_any_cast& e) {
            cout << "\n"
                 << e.what();
        }
        return 0;
    }

    Producción:

    Value:  4.2 
    No value found in var variable
    
  • has_value: esta función miembro se utiliza para verificar si el objeto contiene un valor o no

    Programa:

    // C++ program to demonstrate
    // has_value() method of any class
      
    #include <any>
    #include <iostream>
    #include <string>
      
    int main()
    {
        try {
            any var = 9.5;
            cout << " \n Value:  "
                 << any_cast<double>(var);
      
            if (var.has_value())
                cout << " \n Value found of type "
                     << var.type().name();
        }
      
        catch (bad_any_cast& e) {
            cout << "\n"
                 << e.what();
        }
        return 0;
    }

    Producción:

     Value:  9.5 
     Value found of type d
    
  • type: esta función miembro devuelve una estructura type_info que se puede usar para obtener las propiedades del objeto almacenado.

    Programa:

    // C++ program to demonstrate
    // type() method of any class
      
    #include <any>
    #include <iostream>
    #include <string>
      
    int main()
    {
        try {
            any var = 12.0f;
            cout << " \n Type:  "
                 << var.type().name();
      
            var = "Hello World";
            cout << " \n Type:  "
                 << var.type().name();
        }
        catch (bad_any_cast& e) {
            cout << "\n"
                 << e.what();
        }
        return 0;
    }

    Producción:

    Type:  f 
    Type:  PKc
    

Usos de cualquier

Los usos típicos incluyen

  1. En Bibliotecas, cuando un tipo de biblioteca tiene que contener o pasar algo sin conocer el conjunto de tipos disponibles.
  2. Paso de mensajes
  3. Implementación de bibliotecas de análisis para ej. analizador JSON
  4. Interfaz de usuario: los controles pueden contener cualquier cosa
  5. Sistema de componentes de entidad.

Uno de los principales beneficios de cualquiera es su sustitución viable con void*. void* tiene una capacidad limitada (solo almacena tipos de punteros) y se considera un patrón inseguro.

Manejo de errores:

Hay dos opciones con respecto al manejo de errores para cualquier clase:

  1. Uso de excepciones: bad_any_cast es la excepción lanzada por las formas de retorno de valor de any_cast en caso de discrepancia de tipo.

    Ejemplo:

    // C++ program to demonstrate
    // using exceptions of any class
      
    #include <any>
    #include <iostream>
    #include <string>
      
    int main()
    {
        try {
            any var = 12.0f;
      
            cout << " \n Value:  "
                 << any_cast<double>(var);
        }
      
        catch (bad_any_cast& e) {
            cout << "\n"
                 << e.what();
        }
        return 0;
    }

    Producción

    Value:  
    bad any_cast
    
  2. Devolver un puntero: devolver un puntero es útil cuando el mecanismo de excepción se ha deshabilitado en el compilador. Esta sobrecarga particular de any_cast devuelve el puntero al objeto contenido si la conversión fue exitosa y devuelve nullptr .

    Ejemplo:

    // C++ program to demonstrate
    // returning pointer of any class
      
    #include <any>
    #include <iostream>
    #include <string>
    using namespace std;
      
    int main()
    {
        any var = 12.0f;
      
        // Special Overload of any_cast
        auto* tval = any_cast<float>(&var);
        if (!tval) {
      
            // Type-mismatch
            cout << " \n Bad_any_cast ";
        }
        else {
      
            // Value of the object
            cout << " \n Value:  "
                 << *tval;
        }
      
        return 0;
    }

    Producción:

    Value:  12
    

Consideraciones de memoria

Aunque cualquiera da mucha flexibilidad con el idioma, el problema principal con cualquiera es la asignación de memoria dinámica adicional. Como el contenedor no es consciente del objeto que contiene, la asignación dinámica se convierte en una necesidad para cualquiera.

Pero de acuerdo con el estándar, las implementaciones deben evitar el uso de memoria asignada dinámicamente para un valor contenido pequeño. Ejemplo: donde el objeto construido contiene solo un int. Esta optimización de objetos pequeños solo se aplicará a los tipos T para los que is_nothrow_move_construtible_v verdadero.

Lo que esto normalmente significa es que el compilador debe usar la optimización de búfer pequeño ( SBO ) en la que se debe reservar una cierta cantidad de memoria para el tipo que se va a contener.

En la tabla anterior, se puede ver que la memoria reservada para SBO en algunos casos podría llegar hasta los 64 bytes. (MSVC-64 bits). Esto significa que cada objeto de cualquiera requeriría 64 bytes de memoria reservados incluso si el objeto es pequeño, lo que representa una sobrecarga de memoria considerable.

Aunque any es una característica muy poderosa en C++. Viene con una cantidad considerable de sobrecarga en términos de memoria .

Publicación traducida automáticamente

Artículo escrito por Gopi_Krishna_Menon y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *