Вернуть объект «NULL», если результат поиска не найден.


94

Я новичок в C ++, поэтому, когда учусь, я стараюсь использовать множество Java-измов. В любом случае, в Java, если бы у меня был класс с методом поиска, который возвращал бы объект Tиз a, Collection< T >который соответствует определенному параметру, я бы вернул этот объект, а если бы объект не был найден в коллекции, я бы вернулся null. Тогда в своей вызывающей функции я бы просто проверилif(tResult != null) { ... }

В C ++ я обнаружил, что не могу вернуть nullзначение, если объект не существует. Я просто хочу вернуть «индикатор» типа T, который уведомляет вызывающую функцию о том, что объект не найден. Я не хочу создавать исключение, потому что это не исключительные обстоятельства.

Вот как сейчас выглядит мой код:

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

Как я могу его изменить, чтобы поставить такой маркер?


6
Исключение и NULL - не всегда единственные решения. Часто можно выбрать значение для возврата, указывающее, что оно не найдено: например, std::find(first, last, value)возвращается, lastесли ни один элемент не соответствует.
Cascabel

Ответы:


70

В C ++ ссылки не могут быть нулевыми. Если вы хотите при желании вернуть null, если ничего не найдено, вам нужно вернуть указатель, а не ссылку:

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

В противном случае, если вы настаиваете на возврате по ссылке, вам следует выбросить исключение, если атрибут не найден.

(Между прочим, меня немного беспокоит, что ваш метод constвозвращает не constатрибут. По философским причинам я предлагаю вернуться const Attr *. Если вы также можете изменить этот атрибут, вы можете перегрузить его не- constметодом также возвращает не constатрибут.)


2
Спасибо. Кстати, это общепринятый способ создания такой рутины?
aduric

6
@aduric: Да. Ссылки подразумевают, что результат должен существовать. Указатели подразумевают, что результат может не существовать.
Билл,

7
Просто любопытно, вернемся ли мы теперь nullptrвместо NULLС ++ 11?
Spectral

1
да, всегда используйте nullptr вместо NULL в C ++ 11 и новее. если вам нужна обратная совместимость с более ранними версиями, то не делайте этого
Конрад Джонс

56

Здесь есть несколько возможных ответов. Вы хотите вернуть то, что может существовать. Вот несколько вариантов, от наименее предпочтительных до наиболее предпочтительных:

  • Возврат по ссылке и сигнал о невозможности найти по исключению.

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

Вероятно, что отсутствие атрибутов - нормальная часть выполнения и, следовательно, не очень исключительная. Обработка этого была бы шумной. Нулевое значение не может быть возвращено, поскольку наличие нулевых ссылок является неопределенным поведением.

  • Возврат по указателю

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

Легко забыть проверить, будет ли результат от getAttribute указателем, отличным от NULL, и это простой источник ошибок.

  • Используйте 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&>();
    }

Boost :: optional точно указывает, что здесь происходит, и имеет простые методы проверки того, был ли найден такой атрибут.


Боковое примечание: std :: optional недавно был включен в C ++ 17, так что в ближайшем будущем это будет «стандарт».


+1 Я бы сначала упомянул boost :: optional, а остальные альтернативы лишь кратко упомянул.
Неманья Трифунович

Я видел, что где-то упоминалось boost :: optional, но я подумал, что это требует слишком много накладных расходов. Если его использование - лучший подход к решению подобных проблем, я начну его использовать.
aduric

boost::optionalне требует больших накладных расходов (без динамического распределения), поэтому он так хорош. Использование его с полиморфными значениями требует обертывания ссылок или указателей.
Matthieu M.

2
@MatthieuM. Скорее всего, адурик имел в виду не производительность, а стоимость включения внешней библиотеки в проект.
Swoogan

Дополнение к моему ответу: обратите внимание, что есть движение по стандартизации необязательного в качестве стандартного компонента, вероятно, для того, что вполне может быть C ++ 17. Так что об этой технике стоит знать.
Kaz Dragon

22

Вы можете легко создать статический объект, представляющий возврат 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; 
};

И где-то в исходном файле:

static Attr AttrNull;

Разве NodeNull не должен иметь тип Attr?
aduric

3

Если вам нужно NULLвозвращаемое значение, вам нужно использовать указатели вместо ссылок.

Ссылки сами по себе быть не могут NULL.

(Примечание для будущих плакатов с комментариями: да, вы можете иметь адрес ссылки NULL, если вы действительно пытаетесь это сделать).

Смотрите мой ответ здесь, чтобы узнать о различиях между ссылками и указателями .


2

Как вы уже поняли, вы не можете сделать это так, как делали это на Java (или C #). Вот еще одно предложение: вы можете передать ссылку на объект в качестве аргумента и вернуть значение типа bool. Если результат найден в вашей коллекции, вы можете присвоить его передаваемой ссылке и вернуть «истина», иначе вернуть «ложь». Пожалуйста, обратите внимание на этот код.

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

Вышеупомянутая функция должна найти Оператор по ключу 'token', если она найдет тот, который возвращает true, и присвоить значение параметру Operator & op.

Код вызывающего абонента для этой процедуры выглядит так

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

1

Причина, по которой вы не можете вернуть здесь NULL, заключается в том, что вы объявили свой возвращаемый тип как Attr&. Завершение &делает возвращаемое значение «ссылкой», которая, по сути, представляет собой гарантированно ненулевой указатель на существующий объект. Если вы хотите иметь возможность возвращать значение null, измените значение Attr&на Attr*.


0

Вы не можете вернуться, NULLпотому что возвращаемый тип функции - это объект, referenceа не pointer.


-3

Вы можете попробовать это:

return &Type();

6
Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода.
Натан Оливер,

Вероятно, это возвращает мертвую ссылку на объект в стеке методов, не так ли?
mpromonet
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.