Поверніть об’єкт “NULL”, якщо результат пошуку не знайдено


94

Я досить новачок у C ++, тому я, як правило, займаюся дизайном з великою кількістю Java-ізмів. У будь-якому випадку, в Java, якби у мене був клас із методом 'search', який повертав би об'єкт Tіз 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замість NULLc ++ 11 зараз?
Спектральний

1
так, завжди використовуйте nullptr над NULL у C ++ 11 та пізніших версіях. якщо вам потрібно мати зворотну сумісність з версіями Earliver, тоді цього не потрібно
Конрад Джонс

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

    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

Додаток до моєї відповіді: зверніть увагу на те, що існує крок із стандартизації необов’язкового як компонент std, ймовірно, для того, що цілком може бути 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.

(Примітка до майбутніх плакатів коментарів: Так, ви можете вказати адресу посилання НУЛЬКОЮ, якщо ви справді намагаєтесь це зробити).

Дивіться тут мою відповідь на перелік відмінностей між посиланнями та вказівниками .


2

Як ви вже зрозуміли, що не можете зробити це так, як у Java (або C #). Ось ще одна пропозиція: ви можете передати посилання на об'єкт як аргумент і повернути значення bool. Якщо результат знайдено у вашій колекції, ви можете призначити його переданому посиланню та повернути 'true', інакше повернути 'false'. Будь ласка, розгляньте цей код.

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

Вищезазначена функція повинна знайти Оператора за ключем `` маркер '', якщо вона знаходить той, який повертає істиною, і присвоїти значення параметру Оператор & операція.

Код абонента для цієї процедури виглядає так

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

1

Причиною того, що ви не можете повернути NULL тут, є те, що ви оголосили свій тип повернення як Attr&. Завершення &робить повернене значення "посиланням", яке в основному є гарантованим не нульовим покажчиком на існуючий об'єкт. Якщо ви хочете мати можливість повернути значення null, змініть Attr&на Attr*.


0

Ви не можете повернутися, NULLоскільки тип повернення функції - це об’єкт, referenceа не a pointer.


-3

Ви можете спробувати це:

return &Type();

6
Незважаючи на те, що цей фрагмент коду може вирішити питання, включення пояснення дійсно допомагає поліпшити якість вашої публікації. Пам'ятайте, що ви будете відповідати на запитання для читачів у майбутньому, і ці люди можуть не знати причин вашої пропозиції коду.
NathanOliver

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