Retorna um objeto “NULL” se o resultado da pesquisa não for encontrado

94

Eu sou muito novo em C ++, então tendo a projetar com muitos Java-ismos enquanto estou aprendendo. De qualquer forma, em Java, se eu tivesse uma classe com um método de 'pesquisa' que retornasse um objeto Tde um Collection< T >que correspondesse a um parâmetro específico, eu retornaria esse objeto e se o objeto não fosse encontrado na coleção, eu retornaria null. Então, em minha função de chamada, eu apenas verificariaif(tResult != null) { ... }

Em C ++, estou descobrindo que não posso retornar um nullvalor se o objeto não existir. Eu só quero retornar um 'indicador' do tipo T que notifica a função de chamada que nenhum objeto foi encontrado. Não quero lançar uma exceção porque não é realmente uma circunstância excepcional.

É assim que meu código se parece agora:

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;
}

Como posso alterá-lo para dar esse tipo de marcador?

adúrico
fonte
6
Exceção e NULL nem sempre são as únicas soluções. Freqüentemente, você pode escolher um valor para retornar indicando não encontrado: por exemplo, std::find(first, last, value)retorna lastse nenhum elemento corresponder.
Cascabel

Respostas:

70

Em C ++, as referências não podem ser nulas. Se quiser retornar nulo opcionalmente se nada for encontrado, você precisará retornar um ponteiro, não uma referência:

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

Caso contrário, se você insiste em retornar por referência, deve lançar uma exceção se o atributo não for encontrado.

(A propósito, estou um pouco preocupado com o seu método ser conste retornar um não- constatributo. Por razões filosóficas, sugiro retornar const Attr *. Se você também quiser modificar esse atributo, pode sobrecarregar com um não- constmétodo retornando um não- constatributo também.)

Jesse Beder
fonte
2
Obrigado. A propósito, essa é uma forma aceita de projetar tal rotina?
aduric
6
@aduric: Sim. As referências implicam que o resultado deve existir. Os ponteiros indicam que o resultado pode não existir.
Bill
7
Só por curiosidade, devemos retornar em nullptrvez de NULLpara c ++ 11 agora?
Espectral de
1
sim, sempre use nullptr em vez de NULL em C ++ 11 e posterior. se você precisa ser compatível com as versões anteriores, não o faça
Conrad Jones
56

Existem várias respostas possíveis aqui. Você deseja devolver algo que pode existir. Aqui estão algumas opções, desde a minha menos preferida até a mais preferida:

  • Retorne por referência e o sinal não pode ser encontrado por exceção.

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

É provável que não localizar atributos seja uma parte normal da execução e, portanto, não muito excepcional. O manuseio para isso seria barulhento. Um valor nulo não pode ser retornado porque é um comportamento indefinido ter referências nulas.

  • Retorno por ponteiro

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

É fácil esquecer de verificar se um resultado de getAttribute seria um ponteiro não NULL e é uma fonte fácil de bugs.

  • Use 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&>();
    }

Um boost :: optional significa exatamente o que está acontecendo aqui, e possui métodos fáceis para inspecionar se tal atributo foi encontrado.


Nota lateral: std :: optional foi recentemente votado em C ++ 17, então isso será uma coisa "padrão" em um futuro próximo.

Dragão Kaz
fonte
+1 Eu apenas mencionaria boost :: opcional primeiro, e apenas mencionaria brevemente as outras alternativas.
Nemanja Trifunovic
Sim, eu vi boost :: optional mencionado em algum lugar, mas eu estava pensando que exigia muito overhead. Se usá-lo for a melhor abordagem para esse tipo de problema, vou começar a usá-lo.
aduric
boost::optionalnão envolve muita sobrecarga (sem alocação dinâmica), por isso é tão bom. Usá-lo com valores polimórficos requer referências ou ponteiros de agrupamento.
Matthieu M.
2
@MatthieuM. É provável que a sobrecarga adúrica se referisse não ao desempenho, mas ao custo de incluir uma biblioteca externa no projeto.
Swoogan
Um adendo à minha resposta: observe que há um movimento em andamento para padronizar o opcional como um componente std, provavelmente para o que pode muito bem ser C ++ 17. Portanto, vale a pena conhecer esta técnica.
Kaz Dragon de
22

Você pode criar facilmente um objeto estático que representa um retorno NULL.

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; 
};

E em algum lugar em um arquivo de origem:

static Attr AttrNull;
Mark Ransom
fonte
NodeNull não deveria ser do tipo Attr?
aduric
2

Como você percebeu, não pode fazer da maneira que fazia em Java (ou C #). Aqui está outra sugestão, você poderia passar a referência do objeto como um argumento e retornar o valor bool. Se o resultado for encontrado em sua coleção, você pode atribuí-lo à referência que está sendo passada e retornar 'verdadeiro', caso contrário, retornar '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;
}

A função acima deve encontrar o Operador contra a chave 'token', se encontrar o que retorna verdadeiro e atribuir o valor ao parâmetro Operador & op.

O código do chamador para esta rotina se parece com este

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

O motivo pelo qual você não pode retornar NULL aqui é porque você declarou seu tipo de retorno como Attr&. O trailing &torna o valor de retorno uma "referência", que é basicamente um ponteiro garantido de não ser nulo para um objeto existente. Se você quiser retornar null, mude Attr&para Attr*.

JSB ձոգչ
fonte
0

Você não pode retornar NULLporque o tipo de retorno da função é um objeto referencee não um pointer.

codadicto
fonte
-3

Você pode tentar isto:

return &Type();
Criada
fonte
6
Embora este snippet de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade de sua postagem. Lembre-se de que você está respondendo à pergunta para leitores no futuro e essas pessoas podem não saber os motivos de sua sugestão de código.
NathanOliver
Isso provavelmente retorna uma referência morta para um objeto na pilha de métodos, não é?
mpromonet