2010-06-21 6 views
11

No estoy seguro de si el título de la pregunta es correcto ... Permítanme comenzar explicando mi escenario simple original, y luego paso a explicar qué me gustaría hacer, pero no puedoPolimorfismo de devolución para el valor por transferencia

Originalmente, tenía algo así como:

class Operand; 

Operand genOperandA() { ...; return Operand(); } 
Operand genOperandB() { ...; return Operand(); } 
... // more operand-generation functions 

typedef Operand (*OpGen)(); 

// Table of function pointers 
static const OpGen generators[] = 
{ 
    genOperandA, 
    genOperandB, 
    ... 
}; 

// Function to do some operation on the operand 
void operate(Operand& op); 

... 

// Example call 
operate(generators[1]()); 

Hasta aquí todo bien (creo). Sin embargo, ahora hay varios tipos de operandos derivados, p. class RegisterOperand : public Operand. Tengo nuevas funciones genOperand dedicadas que idealmente devolverían instancias de los tipos derivados. Pero no puedo hacer esto:

Operand genOperandC() { ...; return RegisterOperand(); } 

y yo no puedo hacer esto:

RegisterOperand genOperandC() { ...; return RegisterOperand(); } 

static const OpGen generators[] = 
{ 
    ... 
    genOperandC, 
}; 

Sin embargo, sé que esto funcionaría si tuviera que volver tipos de referencia o puntero, por lo que el único La opción que tengo actualmente es algo así como:

Operand *genOperandC() { ...; return new RegisterOperand(); } 

que ahora requieren una limpieza explícita que no era necesaria originalmente.

¿Alguna alternativa que no haya considerado?

Respuesta

4

Puede haber otros diseños que no requieren el uso de punteros, pero si necesita o desea hacerlo, esto podría interesarle.


Si devolviendo un puntero es un problema (debido a la necesidad de "limpieza" cosas), que sin duda debe considerar el uso de punteros inteligentes como tipo de retorno.

Aquí es un ejemplo de su método de fábrica con punteros inteligentes:

boost::shared_ptr<Operand> genOperandC() 
{ 
    return boost::shared_ptr<Operand>(new RegisterOperand()); 
} 

De esta manera, usted no tendrá que llamar delete manualmente: se hará por el destructor de boost::shared_ptr<Operand> para usted cuando sea necesario.

Si posteriormente, es necesario especificar el puntero resultante, boost proporciona funciones de conversión, así:

boost::shared_ptr<Operand> op = genOperandC(); 

boost::shared_ptr<RegisterOperand> rop = 
    boost::dynamic_pointer_cast<RegisterOperand>(op); 
+1

Use 'boost :: make_shared' en lugar de crear el puntero compartido usted mismo, es un método simple de envoltura pero reduce el número de asignaciones de memoria necesarias (y por lo tanto es más rápido) –

7

se puede envolver:

class Operand 
{ 
public: 

private: 
    std::unique_ptr<OperandImpl> mImpl; 
}; 

Esto es similar a un patrón de estrategia: el operando actual el comportamiento está oculto y es accesible a través de una interfaz no virtual. El usuario obtiene una copia de Operand, no necesita saber nada sobre su interno y puede usarlo, y usted es libre de implementar varios comportamientos derivados.

+0

¿La idea aquí heredará mis diferentes subtipos de 'OperandImpl', y que las llamadas de miembros a' Operando' simplemente deberían ser delegadas a '* mImpl'? –

+0

Sí, este es un patrón de estrategia clásica, donde el manejo de memoria real se oculta desde el cliente (en lugar de devolver un puntero, inteligente o no, a 'OperandImpl'). Sin embargo, puede hacer algún trabajo antes de delegar (comprobación de argumentos, el registro etc ...) –

0

¿Qué tal algo así?

Tenga en cuenta que simplemente no puede operar (generadores [i]()) como el original operar() tomar una referencia no const.

#include <iostream> 
#include <string> 
#include <memory> 

class Operand { 
public: 
     Operand(std::string x = "Operand"): x(x) {} 
     const std::string x; 
}; 

class RegisterOperand: public Operand { 
public: 
     RegisterOperand(std::string x = "RegisterOperand") 
       : Operand(x) {} 
}; 

typedef std::auto_ptr<Operand> POperand; 

POperand genOperandA() { return POperand(new Operand("genOperandA")); } 
POperand genOperandB() { return POperand(new Operand("genOperandB")); } 
POperand genOperandC() { return POperand(new RegisterOperand()); } 
// more operand-generation functions 

typedef POperand (*OpGen)(); 

// Table of function pointers 
static const OpGen generators[] = 
{ 
     genOperandA, 
     genOperandB, 
     genOperandC, 
}; 

void operate(const POperand& op) 
{ 
     std::cout << op->x << std::endl; 
} 

int main() 
{ 
     operate(generators[0]()); 
     operate(generators[1]()); 
     operate(generators[2]()); 
     return 0; 
} 
+0

El uso de 'auto_ptr' está en desuso en C++ 0x. Prefiere usar 'unique_ptr' en su lugar. Si aún no tiene acceso a C++ 0x, entonces prefiera 'shared_ptr'. –

+1

@Matthieu: Aunque me gusta mucho unique_ptr, me gustaría ser reacios a utilizarlo porque se basa en una característica del lenguaje C++ 0x. Creo que es apropiado el uso de std :: auto_ptr si usted sabe lo que está haciendo y no quieren la sobrecarga de recuento de referencias de shared_ptr. Solo mis 2 centavos. – sellibitze

0

Sé que esta pregunta se le preguntó hace algún tiempo, pero recientemente me encontré con este problema yo mismo y se le ocurrió una solución diferente que yo, aunque podría ser útil aquí.

Así que la idea es hacer un envoltorio para manejar el puntero, sino también para apoyar la copia de la envoltura a diferencia del puntero única que sólo se puede mover.

class PolymorphicType { 
public: 
    /* 
     Class interface ... 
    */ 
    PolymorphicType() { it = nullptr; } 
    virtual ~PolymorphicType() { if(it != nullptr) delete it; }   

    PolymorphicType& operator=(const PolymorphicType& org) { 
      //Clone the derived type and store it 
      if(org.it != nullptr) 
       it = org.it->clone(); 
    } 
private: 
    Base* it; 
}; 

Cada clase derivada ahora debe implementar su propio método clone y que son buenos para ir! Y por si acaso hay una buena publicación que explica cómo funciona la clonación de tipos derivados: Copying derived entities using only base class pointers, (without exhaustive testing!) - C++

Espero que esto ayude a alguien!

Cuestiones relacionadas