Подтвердить что ты не робот

Проверка нулевого объекта в С++

Я в основном работал только с 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 */
}

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

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-стиль для объекта, есть причины выбрать один над другим?

4b9b3361

Ответ 1

В принципе, все, что я пытаюсь сделать, это предотвращать сбои программы, когда some_cpp_function() вызывается с NULL.

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

  • Каков наиболее типичный/распространенный способ сделать это с объектом С++ (который не связан с перегрузкой оператора ==)?
  • Это даже правильный подход? то есть. не следует ли писать функции, которые принимают объект в качестве аргумента, а скорее записывают функции-члены? (Но даже если это так, ответьте на исходный вопрос.)

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

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

Если вам нужно представить "no object", тогда передайте указатель на функцию и пусть этот указатель будет 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;
}

Если "no object" никогда не нужно представлять, то ссылки работают нормально. Фактически, перегрузки операторов намного проще, потому что они перегружают.

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