Проверка на нулевой объект в C ++ - PullRequest
31 голосов
/ 20 января 2010

В основном я работал только с C и столкнулся с некоторыми незнакомыми проблемами в C ++.

Скажем, у меня есть такая функция в Си, которая была бы очень типичной:

int some_c_function(const char* var)
{
    if (var == NULL) {
        /* Exit early so we don't dereference a null pointer */
    }
    /* The rest of the code */
}

И скажем, я пытаюсь написать аналогичную функцию на C ++:

int some_cpp_function(const some_object& str)
{
    if (str == NULL)  // This doesn't compile, probably because some_object doesn't overload the == operator

    if (&str == NULL) // This compiles, but it doesn't work, and does this even mean anything?
}

По сути, все, что я пытаюсь сделать - это предотвратить сбой программы при вызове some_cpp_function () с NULL.

  • Каков наиболее типичный / распространенный способ сделать это с объектом C ++ (без перегрузки оператора ==)?

  • Это даже правильный подход? То есть я должен писать не функции, которые принимают объект в качестве аргумента, а писать функции-члены? (но даже если так, пожалуйста, ответьте на оригинальный вопрос)

  • Между функцией, которая берет ссылку на объект, или функцией, которая берет указатель в стиле C на объект, есть ли причины выбирать одну из них?

Ответы [ 8 ]

33 голосов
/ 20 января 2010

По сути, все, что я пытаюсь сделать, это предотвратить сбой программы при some_cpp_function () вызывается с NULL.

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

14 голосов
/ 20 января 2010

Ссылка не может быть NULL. Интерфейс заставляет вас передавать реальный объект в функцию.

Таким образом, нет необходимости проверять NULL. Это одна из причин, по которой ссылки были введены в C ++.

Обратите внимание, что вы все еще можете написать функцию, которая принимает указатель. В этой ситуации вам все еще нужно проверить на NULL. Если значение равно NULL, вы возвращаетесь рано, как в C. Примечание: вы не должны использовать исключения, когда указатель равен NULL Если параметр никогда не должен иметь значение NULL, вы создаете интерфейс, который использует ссылку.

5 голосов
/ 20 января 2010

Как все говорили, ссылки не могут быть нулевыми.Это потому, что ссылка относится к объекту.В вашем коде:

// this compiles, but doesn't work, and does this even mean anything?
if (&str == NULL)

вы берете адрес объекта str.По определению str существует, поэтому у него есть адрес.Таким образом, это не может быть NULL.Таким образом, синтаксически, вышеприведенное верно, но логически условие if всегда будет ложным.

О ваших вопросах: это зависит от того, что вы хотите сделать.Вы хотите, чтобы функция могла изменять аргумент?Если да, передайте ссылку.Если нет, не делайте (или передайте ссылку на const).См. этот C ++ FAQ для некоторых хороших деталей.

Как правило, в C ++ передача по ссылке предпочитается большинством людей, а не указателем.Одной из причин является именно то, что вы обнаружили: ссылка не может быть NULL, что позволяет избежать головной боли при проверке ее в функции.

5 голосов
/ 20 января 2010

Ссылка на C ++ не является ни указателем, ни ссылкой на стиль Java / C # и не может быть NULL. Они ведут себя так, как будто они являются псевдонимами другого существующего объекта.

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

То есть, я видел, как проверка кода на 'нулевые ссылки' выполнялась примерно так: if ( &reference == 0 ), но стандарт ясен, что в правильно сформированной программе не может быть нулевых ссылок. Если ссылка связана с нулевым объектом, программа плохо сформирована и должна быть исправлена. Если вам нужны необязательные значения, используйте указатели (или некоторые конструкции более высокого уровня, такие как boost::optional), а не ссылки.

4 голосов
/ 09 июля 2012

Вы можете использовать специальный обозначенный объект в качестве нулевого объекта в случае ссылок следующим образом:

class SomeClass
{
    public:

        int operator==(SomeClass &object)
        {
            if(this == &object) 
            {
                    return true;
            }

            return false;
        }


    static SomeClass NullObject;
};

SomeClass SomeClass::NullObject;

void print(SomeClass &val)
{
    if(val == SomeClass::NullObject)
    {
        printf("\nNULL");
    }
    else
    {
        printf("\nNOT NULL");
    }
}
2 голосов
/ 20 января 2010
  • Каков наиболее типичный / распространенный способ сделать это с объектом C ++ (без перегрузки оператора ==)?
  • Это даже правильный подход? то есть. я должен не писать функции, которые принимают объект как аргумент, а писать функции-члены? (Но даже если так, пожалуйста, ответьте на оригинальный вопрос.)

Нет, ссылки не могут быть нулевыми (если только не определено поведение, в этом случае все ставки уже отменены). Нужно ли вам писать метод или не метод, зависит от других факторов.

  • Между функцией, которая получает ссылку на объект, или функцией, которая принимает указатель в стиле C на объект, есть ли причины выбирать одну из них над другой?

Если вам нужно представить «нет объекта», передайте указатель на функцию, и пусть этот указатель будет NULL:

int silly_sum(int const* pa=0, int const* pb=0, int const* pc=0) {
  /* Take up to three ints and return the sum of any supplied values.

  Pass null pointers for "not supplied".

  This is NOT an example of good code.
  */
  if (!pa && (pb || pc)) return silly_sum(pb, pc);
  if (!pb && pc) return silly_sum(pa, pc);
  if (pc) return silly_sum(pa, pb) + *pc;
  if (pa && pb) return *pa + *pb;
  if (pa) return *pa;
  if (pb) return *pb;
  return 0;
}

int main() {
  int a = 1, b = 2, c = 3;
  cout << silly_sum(&a, &b, &c) << '\n';
  cout << silly_sum(&a, &b) << '\n';
  cout << silly_sum(&a) << '\n';
  cout << silly_sum(0, &b, &c) << '\n';
  cout << silly_sum(&a, 0, &c) << '\n';
  cout << silly_sum(0, 0, &c) << '\n';
  return 0;
}

Если «никакой объект» никогда не нужно представлять, то ссылки работают нормально. На самом деле, перегрузки операторов намного проще, потому что они принимают перегрузки.

Вы можете использовать что-то вроде boost :: необязательный.

2 голосов
/ 20 января 2010

C ++ ссылки, естественно, не могут быть нулевыми, вам не нужна проверка. Функция может быть вызвана только путем передачи ссылки на существующий объект.

2 голосов
/ 20 января 2010

Вы должны использовать NULL только с указателями.Ваша функция принимает ссылку, и они не могут быть NULL.

Напишите вашу функцию так же, как вы написали бы ее в C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...