2010-04-23 19 views
16

Si tengo un constructor con 2 parámetros obligatorios y 4 parámetros opcionales, cómo puedo evitar escribir 16 constructores o incluso los 10 o más constructores que tendría que escribir si utilizaba parámetros predeterminados (que no me gusta porque es una auto documentación pobre) ¿Hay algún modismo o método utilizando plantillas que pueda usar para que sea menos tedioso? (¿Y más fácil de mantener?)evitando el tedio de los parámetros opcionales

+1

¿Estás seguro de que tu clase no está tratando de hacer demasiado? –

+0

@Johannes: Quiere la posibilidad de omitir say d sin forzar a la persona que llama a conocer su valor predeterminado. – Danvil

+2

@John: puedo ver a qué te refieres, pero los parámetros pueden ser propiedades. Por ejemplo, tengo una clase de botón GUI que tiene muchas propiedades, como imagen, texto, tamaño, color, etc., pero no creo que sea "demasiado". En mi humilde opinión, los botones tienen un propósito muy específico y son fáciles de encajar en una sola clase en la que el usuario sabrá de inmediato qué hace la clase. – Kyle

Respuesta

34

Puede que le interese el Named Parameter Idiom.

Para resumir, cree una clase que contenga los valores que desea pasar a su constructor (es). Agregue un método para establecer cada uno de esos valores, y haga que cada método haga un return *this; al final. Tener un constructor en tu clase que tome una referencia directa a esta nueva clase. Esto se puede usar de la siguiente manera:

class Person; 

class PersonOptions 
{ 
    friend class Person; 
    string name_; 
    int age_; 
    char gender_; 

public: 
    PersonOptions() : 
    age_(0), 
    gender_('U') 
    {} 

    PersonOptions& name(const string& n) { name_ = n; return *this; } 
    PersonOptions& age(int a) { age_ = a; return *this; } 
    PersonOptions& gender(char g) { gender_ = g; return *this; } 
}; 

class Person 
{ 
    string name_; 
    int age_; 
    char gender_; 

public: 
    Person(const PersonOptions& opts) : 
    name_(opts.name_), 
    age_(opts.age_), 
    gender_(opts.gender_) 
    {} 
}; 
Person p = PersonOptions().name("George").age(57).gender('M'); 
+0

¿Cómo harías ese trabajo en un constructor? ¿Usando 'return this' en el método constructor? –

+1

Obviamente, no puede devolver nada de un constructor. Pero puede usar una copia, como 'Foo f = Foo(). Option1(). Option2();' por ejemplo. El ejemplo en el enlace usa una clase separada para hacer la creación real. –

+2

@Robert es importante notar que el lado derecho comienza con 'PersonOptions', no' Person'. Esto crea un objeto supuestamente liviano y luego tiene varias llamadas a métodos para establecer propiedades a valores no predeterminados. Luego hay un ctor llamado 'Persona (const PersonOptions &)' y ta da. La instanciación de un objeto (por ejemplo, 'PersonOptions()') deja "* this" en el contexto actual para que puedas .method() de inmediato. Sin embargo, cada método todavía necesita 'devolver * esto' para poder encadenarlo. –

9

¿Qué pasa si hace un objeto de parámetro que contenga todos los campos? Entonces podrías pasar eso y simplemente establecer los campos que necesites. Probablemente hay un nombre para ese patrón, no está seguro de lo que es aunque ...

ACTUALIZACIÓN:

Código podría parecer un poco esto:

paramObj.x=1; 
paramObj.y=2; 
paramObj.z=3; 
paramObj.magic=true; 
... //set many other "parameters here" 

someObject myObject = new someObject(paramObj); 

y dentro del constructor Puede someObject establecer valores predeterminados para las cosas que no estaban ya establecidas (o generar un error si era obligatorio).

Honestamente, no soy un gran admirador de esta solución, pero la he usado una o dos veces cuando paramObj tiene sentido al contener un conjunto de datos que generalmente van juntos (para que podamos usarlo durante más de solo constructores), y era mejor que múltiples constructores. Descubrí que era feo pero funcionó, YMMV.

+2

... y este objeto de parámetro establecería valores predeterminados en su constructor predeterminado. – Danvil

+1

¿Por qué es mejor que construir el objeto original y las propiedades de configuración? –

+0

@Johannes: ¿No es solo la parábola de parámetros nombrados que Fred Larson menciona en su respuesta? –

4

Y ahora para el "Boost tiene algo para que" la respuesta:

El Boost Parameter Library parece ser un buen ajuste a su caso de uso.

1

Todo nuevo para C++ 17

#include <optional> 

using optional_int = std::optional<int>; 

class foo { 
    int arg0, arg1; // required 
    int arg2, arg3; // optional 
    const int default_2 = -2; 
    const int default_3 = -3; 
public: 
    foo(int arg0, int arg1, optional_int opt0 = {}, optional_int opt1 = {}) 
     : arg0(arg0), arg1(arg1) 
     , arg2(opt0.value_or(default_2)) 
     , arg3(opt1.value_or(default_3)) 
    { } 

}; 

int main() { 
    foo bar(42, 43, {}, 45); // Take default for opt0 (arg2) 
    return 0; 
} 

I tienen una implementación spline cúbica que permite al usuario opcionalmente para especificar la primera derivada, ya sea en el extremo izquierdo, el extremo derecho, o ambos. Si no se especifica una derivada, entonces el código en efecto calcula una, al asumir que la segunda derivada es cero (la llamada "spline natural"). Aquí hay un fragmento para el extremo izquierdo.

// Calculate the second derivative at the left end point 
    if (!left_deriv.has_value()) { 
     ddy[0]=u[0]=0.0; // "Natural spline" 
    } else { 
     const real yP0 = left_deriv.value(); 
     ddy[0] = -0.5; 
     u[0]=(3.0/(x[1]-x[0]))*((y[1]-y[0])/(x[1]-x[0])-yP0); 
    } 
Cuestiones relacionadas