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?


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.)


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.


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

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

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*.


0

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


-3

Você pode tentar isto:

return &Type();

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.