Comme je suis assez nouveau en C++, j'ai tendance à concevoir avec beaucoup de Java-isms pendant que j'apprends. Quoi qu'il en soit, en Java, si j'avais une classe avec une méthode 'search' qui retournerait un objet T
à partir d'un Collection< T >
Qui correspondait à un paramètre spécifique, je retournerais cet objet et si l'objet était non trouvé dans la collection, je retournerais null
. Ensuite, dans ma fonction d'appel, je vérifierais simplement if(tResult != null) { ... }
En C++, je découvre que je ne peux pas retourner de valeur null
si l'objet n'existe pas. Je veux juste renvoyer un "indicateur" de type T qui indique à la fonction appelante qu'aucun objet n'a été trouvé. Je ne veux pas faire d'exception car ce n'est pas vraiment une circonstance exceptionnelle.
Voici à quoi ressemble mon code:
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;
}
Comment puis-je le changer afin que je puisse donner ce type de marqueur?
En C++, les références ne peuvent pas être nulles. Si vous souhaitez éventuellement renvoyer null si rien n'est trouvé, vous devez renvoyer un pointeur, pas une référence:
Attr *getAttribute(const string& attribute_name) const {
//search collection
//if found at i
return &attributes[i];
//if not found
return nullptr;
}
Sinon, si vous insistez pour retourner par référence, vous devriez alors lever une exception si l'attribut n'est pas trouvé.
(Au fait, je crains un peu que votre méthode soit const
et renvoie un attribut non -const
. Pour des raisons philosophiques, je suggérerais de renvoyer const Attr *
. Si vous souhaitez également modifier cet attribut, vous pouvez surcharger avec une méthode non -const
renvoyant également un attribut non -const
.)
Il y a plusieurs réponses possibles ici. Vous voulez retourner quelque chose qui pourrait exister. Voici quelques options, allant du moins préféré au plus préféré:
Retour par référence et signal impossible à trouver par exception.
Attr& getAttribute(const string& attribute_name) const
{
//search collection
//if found at i
return attributes[i];
//if not found
throw no_such_attribute_error;
}
Il est probable que le fait de ne pas trouver d'attributs fasse partie de l'exécution normale et n'est donc pas très exceptionnel. La manipulation de ce serait bruyant. Une valeur NULL ne peut pas être renvoyée car son comportement non défini consiste à avoir des références NULL.
Retour par pointeur
Attr* getAttribute(const string& attribute_name) const
{
//search collection
//if found at i
return &attributes[i];
//if not found
return nullptr;
}
Il est facile d'oublier de vérifier si le résultat de getAttribute serait un pointeur non NULL et constituerait une source facile de bogues.
Utilisez 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 boost :: optional signifie exactement ce qui se passe ici et propose des méthodes simples pour vérifier si un tel attribut a été trouvé.
Note latérale: std :: optional a récemment été voté en C++ 17, ce sera donc une chose "standard" dans un avenir proche.
Vous pouvez facilement créer un objet statique qui représente un retour 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;
};
Et quelque part dans un fichier source:
static Attr AttrNull;
Si vous voulez une valeur de retour NULL
, vous devez utiliser des pointeurs au lieu de références.
Les références ne peuvent pas être elles-mêmes NULL
.
(Remarque pour les futures affiches de commentaires: Oui, vous pouvez indiquer l'adresse d'une référence si la valeur est NULL si vous essayez vraiment de le faire).
Voir ma réponse ici pour une liste des différences entre les références et les pointeurs .
Comme vous l'avez compris, vous ne pouvez pas le faire comme vous l'avez fait avec Java (ou C #). Voici une autre suggestion. Vous pouvez transmettre la référence de l'objet sous forme d'argument et la renvoyer. Valeur booléenne. Si le résultat est trouvé dans votre collection, vous pouvez l'affecter à la référence passée et renvoyer 'true', sinon, 'false'. Veuillez considérer ce code.
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 fonction ci-dessus doit rechercher l'opérateur contre la clé 'token', si elle trouve celle qu'il renvoie, et attribuer la valeur au paramètre Operator & op.
Le code de l'appelant pour cette routine ressemble à ceci
Operator opr;
if (OperatorList::tryGetOperator(strOperator, opr))
{
//Do something here if true is returned.
}
La raison pour laquelle vous ne pouvez pas renvoyer NULL ici est que vous avez déclaré votre type de retour comme Attr&
. La fuite &
fait de la valeur de retour une "référence", qui est fondamentalement un pointeur sur-un-objet-garanti. Si vous voulez pouvoir retourner null, changez Attr&
à Attr*
.
Vous ne pouvez pas renvoyer NULL
car le type de retour de la fonction est un objet reference
et non un pointer
.