2010-04-14 14 views
83

Soy bastante nuevo en C++, así que tiendo a diseñar con muchos ismos de Java mientras estoy aprendiendo. De todos modos, en Java, si tuviera clase con un método de 'búsqueda' que devolviera un objeto T desde un Collection< T > que coincidiera con un parámetro específico, devolvería ese objeto y si el objeto no se encontraba en la colección, devolvería null . Luego, en mi función de llamada simplemente verificaría if(tResult != null) { ... }Devuelve un objeto "NULO" si no se encuentra el resultado de la búsqueda

En C++, estoy descubriendo que no puedo devolver un valor null si el objeto no existe. Solo quiero devolver un 'indicador' de tipo T que notifica a la función de llamada que no se ha encontrado ningún objeto. No quiero lanzar una excepción porque no es realmente una circunstancia excepcional.

Esto es lo que mi código es el momento:

class Node { 
    Attr& getAttribute(const string& attribute_name) const { 
     //search collection 
     //if found at i 
      return attributes[i]; 
     //if not found 
      return NULL; // what should this be? 
    } 

private: 
    vector<Attr> attributes; 
} 

¿Cómo puedo cambiarlo por lo que puedo dar ese tipo de marcador?

+5

Excepción y NULL no siempre son las únicas soluciones. A menudo puede elegir un valor para devolver que indica que no se encontró: por ejemplo, 'std :: find (first, last, value)' devuelve 'last' si no coincide ningún elemento. – Cascabel

Respuesta

57

En C++, las referencias no pueden ser nulas. Si desea opcionalmente return null si no se encuentra nada, tiene que devolver un puntero, no una referencia:

Attr *getAttribute(const string& attribute_name) const { 
    //search collection 
    //if found at i 
     return &attributes[i]; 
    //if not found 
     return nullptr; 
} 

De lo contrario, si usted insiste en retorno por referencia, a continuación, usted debe lanzar una excepción si el ISN atributo no encontrado.

(Por cierto, estoy un poco preocupado por ser el método de const y devolver un atributo no const. Por razones filosóficas, sugeriría volver const Attr *. Si usted también puede querer modificar este atributo, puede sobrecargar con un const método no devolver un atributo no const también.)

+2

Gracias. Por cierto, ¿es esta una forma aceptada de diseñar tal rutina? – aduric

+5

@aduric: Sí. Las referencias implican que el resultado debe existir. Los indicadores implican que el resultado podría no existir. – Bill

+6

Simplemente curioso, ¿deberíamos devolver 'nullptr' en lugar de' NULL' para C++ 11 ahora? – 8090PZ

0

Usted no puede regresar NULL debido a que el tipo de retorno de la función es un objeto reference y no un pointer.

1

La razón por la que no puede devolver NULL aquí es porque ha declarado su tipo de devolución como Attr&. El & final hace que el valor de retorno sea una "referencia", que básicamente es un puntero garantizado que no debe ser nulo a un objeto existente. Si desea poder devolver nulo, cambie Attr& a Attr*.

50

Aquí hay varias respuestas posibles. Quieres devolver algo que pueda existir. Aquí están algunas opciones, que van desde menos mi preferido a los más preferidos:

  • retorno por referencia, y la señal puede-no-encontrar por excepción.

    Attr& getAttribute(const string& attribute_name) const 
    { 
        //search collection 
        //if found at i 
         return attributes[i]; 
        //if not found 
         throw no_such_attribute_error; 
    }

Es probable que no encontrar atributos es una parte normal de la ejecución, y por lo tanto no es muy excepcional. El manejo de esto sería ruidoso. No se puede devolver un valor nulo porque su comportamiento indefinido tiene referencias nulas.

  • Regreso en puntero

    Attr* getAttribute(const string& attribute_name) const 
    { 
        //search collection 
        //if found at i 
         return &attributes[i]; 
        //if not found 
         return nullptr; 
    }

Es fácil olvidar para comprobar si un resultado de getAttribute sería un puntero no nulo, y es una fuente fácil de errores.

  • Uso Boost.Optional

    boost::optional<Attr&> getAttribute(const string& attribute_name) const 
    { 
        //search collection 
        //if found at i 
         return attributes[i]; 
        //if not found 
         return boost::optional<Attr&>(); 
    }

Un impulso :: opcionales significa exactamente lo que está pasando aquí, y cuenta con métodos fáciles para inspeccionar si se ha encontrado tal atributo.


Nota al margen: std :: opcional fue recientemente votado en C++ 17, por lo que esta será una cosa "estándar" en un futuro próximo.

+0

+1 Solo mencionaría boost :: optional primero, y solo mencionaré brevemente las otras alternativas. –

+0

Ya vi boost :: opcional mencionado en alguna parte, pero estaba pensando que requería demasiada sobrecarga. Si usarlo es el mejor enfoque para este tipo de problemas, comenzaré a usarlo. – aduric

+0

'boost :: optional' no implica demasiados gastos generales (sin asignación dinámica), por lo que es tan bueno. Usarlo con valores polimórficos requiere envolver referencias o punteros. –

22

Puede crear fácilmente un objeto estático que representa un retorno NULO.

class Attr; 
extern Attr AttrNull; 

class Node { 
.... 

Attr& getAttribute(const string& attribute_name) const { 
    //search collection 
    //if found at i 
     return attributes[i]; 
    //if not found 
     return AttrNull; 
} 

bool IsNull(const Attr& test) const { 
    return &test == &AttrNull; 
} 

private: 
    vector<Attr> attributes; 
}; 

Y en algún lugar de un archivo de origen:

static Attr AttrNull; 
+0

¿No debería NodeNull ser del tipo Attr? – aduric

+0

Buen punto, voy a arreglar el ejemplo. –

-4

Puede probar esto:

return &Type(); 
+6

Si bien este fragmento de código puede resolver la pregunta, [incluyendo una explicación] (http://meta.stackexchange.com/questions/114762/explaining-entirely-cob-based-answers) realmente ayuda a mejorar la calidad de su enviar. Recuerde que usted está respondiendo la pregunta a los lectores en el futuro, y es posible que esas personas no sepan los motivos de su sugerencia de código. – NathanOliver

+0

Esto probablemente devuelva una referencia muerta a un objeto en la pila de métodos, ¿no es así? – mpromonet

+0

exactamente. lllllll – Created

2

Como se han dado cuenta de que no se puede hacer de la manera que lo han hecho en Java (o C#). Aquí hay otra sugerencia, podría pasar la referencia del objeto como argumento y devolver el valor de bool. Si el resultado se encuentra en su colección, puede asignarlo a la referencia que se pasa y devolver 'verdadero'; de lo contrario, devolver 'falso'. Por favor considere este código.

typedef std::map<string, Operator> OPERATORS_MAP; 

bool OperatorList::tryGetOperator(string token, Operator& op) 
{ 
    bool val = false; 

    OPERATORS_MAP::iterator it = m_operators.find(token); 
    if (it != m_operators.end()) 
    { 
     op = it->second; 
     val = true; 
    } 
    return val; 
} 

La función anterior tiene que encontrar el operador con la clave 'simbólico', si encuentra una que devuelve verdadero y asignar el valor al parámetro del operador & op.

El código de llamada para esta rutina se ve así

Operator opr; 
if (OperatorList::tryGetOperator(strOperator, opr)) 
{ 
    //Do something here if true is returned. 
} 
Cuestiones relacionadas