Problema con Single Argument Constructor en C++ y cómo resolverlo

En C++, si una clase tiene un constructor al que se puede llamar con un solo argumento, este constructor se convierte en un constructor de conversión porque dicho constructor permite la conversión automática a la clase que se está construyendo. 

Problema:
Siempre que hay un constructor con un solo argumento y hay una función que toma un argumento del mismo tipo de clase pero cuando se llama a esta función usando un tipo de argumento igual al del constructor, en ese caso, se llama a la función exitosamente. Esto se debe a que el constructor convierte implícitamente el argumento al tipo de clase. El argumento se pasa al constructor y luego se ejecuta la función. Esto es algo que no esperaríamos. 

A continuación se muestra el programa C++ que demuestra el problema anterior:

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
    int data;
  
public:
    // Constructor with single parameter
    GfG(int a)
        : data(a)
    {
    }
  
    // Default constructor
    GfG() {}
  
    // Defining function to print
    // the value of data member
    void display()
    {
        cout << "Value of data is: " << data;
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    int var = 10;
  
    // Function gets called even if
    // int type argument is passed
    func(var);
}
Producción

Value of data is: 10

Explicación:
En el código anterior, hay un constructor definido por el usuario que toma un argumento de tipo GFG (tipo de clase) y hay una función que también toma un argumento de tipo de clase. Cuando hay un intento de invocar la función pasando el parámetro de tipo int, en este caso, la función se llama con éxito. Esto sucede debido al constructor definido por el usuario. El valor int pasado a la función se convierte implícitamente en tipo de clase y el miembro de datos se inicializa con el valor del parámetro pasado (var).

Solución: 
Para evitar este problema de conversión implícita es hacer explícito el constructor. A continuación se muestra el programa C++ para demostrar la solución al problema de conversión implícita:

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
    int data;
  
public:
    // Constructor with single parameter
    // declared explicit
    explicit GfG(int a)
        : data(a)
    {
    }
  
    // Default constructor
    GfG() {}
  
    // Function to print value
    // of data member
    void display()
    {
        cout << "Value of data is: " << data;
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    int var = 10;
  
    // This function call results
    // in error
    func(var);
}

Producción:

Inconveniente:
Este enfoque, sin embargo, tiene algunos inconvenientes. ¿Qué pasa si en el mismo programa el usuario realmente quiere convertir el tipo de datos int al tipo de datos de clase y asignar un valor int a un objeto de la clase? La siguiente asignación dará como resultado un error-

int var = 10;
GfG obj = var;  // This will result in error

Para resolver este problema, puede convertir explícitamente var a GfG y luego asignarlo a obj.

GfG obj = (GfG) var; // This works fine

A continuación se muestra el programa C++ para implementar el enfoque anterior:

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
    int data;
  
public:
    // Constructor with single parameter
    // declared explicit
    explicit GfG(int a)
        : data(a)
    {
    }
  
    // Default constructor
    GfG() {}
  
    // Function to print value
    // of data member
    void display()
    {
        cout << "Value of data is: " << data;
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    int var = 10;
  
    // Explicitly converting var to
    // class (GfG) type
    GfG obj = (GfG)var;
  
    // Calling function with the
    // converted variable obj
    func(obj);
}
Producción

Value of data is: 10

Consideremos un ejemplo más y analicemos qué sucede cuando lo explícito tampoco funciona.

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
private:
    string str;
  
public:
    // Constructor with single parameter
    // declared explicit
    GfG(int a)
    {
        str.resize(a);
    }
  
    // Default constructor
    GfG(const char* string)
    {
        str = string;
    }
  
    // Function to print value
    // of data member
    void display()
    {
        cout << "String is: " << str << "\n";
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    // This line will compile and
    // will use GFG(int)
    GfG obj = 'x';
  
    // Calling function with the
    // converted variable obj
    func(obj);
    return 0;
}

Producción

String is:

Explicación:
en el ejemplo anterior, el usuario intenta inicializar una string con un valor char pero char es parte de la familia de enteros, por lo que la compilación usará el constructor GfG(int) para convertir implícitamente char a GfG. Esto producirá resultados inesperados. 
Como se discutió anteriormente, una de las soluciones a este problema es usar la palabra clave explícita.

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
private:
    string str;
  
public:
    // Constructor with single parameter
    // declared explicit
    explicit GfG(int a)
    {
        str.resize(a);
    }
  
    // Default constructor
    GfG(const char* string)
    {
        str = string;
    }
  
    // Function to print value
    // of data member
    void display()
    {
        cout << "String is: " << str << "\n";
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    // Compile-time error since
    // GfG(int) is explicit, so
    // nothing will match.
    GfG obj = 'x';
  
    // Calling function with the
    // converted variable obj
    func(obj);
    return 0;
}

Producción:

output explicit

Explicación:
el programa anterior no compilará, ya que GfG(int) se hizo explícito y no se pudo encontrar un constructor de conversión adecuado para convertir implícitamente ‘x’ en GfG. Tenga en cuenta que las palabras clave explícitas solo pueden rechazar conversiones implícitas, el encasillamiento no se puede evitar usando la palabra clave explícita como se discutió anteriormente.

La palabra clave eliminar

Una solución parcial al problema anterior es crear un constructor privado GfG(char). A continuación se muestra el programa C++ para implementar este concepto:

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
private:
    string str;
  
    // Objects of type char cannot
    // be constructed from outside
    // the class
    GfG(char)
    {
    }
  
public:
    // Constructor with single parameter
    // declared explicit
    explicit GfG(int a)
    {
        str.resize(a);
    }
  
    // Default constructor
    GfG(const char* string)
    {
        str = string;
    }
  
    // Function to print value
    // of data member
    void display()
    {
        cout << "String is: " << str << "\n";
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    // Compile-time error since
    // GfG(char) is private
    GfG obj = 'x';
  
    // Calling function with the
    // converted variable obj
    func(obj);
    return 0;
}

Producción:

Output GFGchar private

Explicación:
En el código anterior, el constructor GfG(char) se hace privado. Esto ha impedido el acceso al constructor desde fuera de la clase, pero aún se puede usar dentro de la clase. La solución a este problema es utilizar la palabra clave delete.

Eliminar palabra clave:
a continuación se muestra el programa C++ para implementar el concepto de eliminar palabra clave:

C++

// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
  
// Defining the class
class GfG {
private:
    string str;
  
    // Any use of this constructor
    // is an error
    GfG(char) = delete;
  
public:
    // Constructor with single parameter
    // declared explicit
    explicit GfG(int a)
    {
        str.resize(a);
    }
  
    // Default constructor
    GfG(const char* string)
    {
        str = string;
    }
  
    // Function to print value
    // of data member
    void display()
    {
        cout << "String is: " << str << "\n";
    }
};
  
// User-defined function that takes
// object of class GfG as argument
void func(GfG o)
{
    o.display();
}
  
// Driver code
int main()
{
    // Compile-time error since
    // GfG(char) is deleted
    GfG obj = 'x';
  
    // Calling function with the
    // converted variable obj
    func(obj);
    return 0;
}

Producción:

Output delete

Explicación:
cuando se elimina una función, cualquier uso de esa función es un error de tiempo de compilación.

Publicación traducida automáticamente

Artículo escrito por husain0803 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 *