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

Nullptr и проверка, указывает ли указатель на действительный объект

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

object * meh = new object;
if(meh) 
    meh->member;

Или, когда мне нужно было безопасно удалить объект, что-то вроде этого

if(meh)
{
    delete meh;
    meh = 0;
}

Хорошо, теперь я узнал о проблемах, которые могут возникнуть в результате использования объектов и указателей в булевых выражениях как с буквальными числами, так и с жестким путем:. И теперь я также узнал о не столь новой, но довольно крутой функции С++, ключевого слова nullptr. Но теперь мне интересно.

Я уже прошел и пересмотрел большую часть моего кода, чтобы, например, при удалении объектов я теперь пишу

if(meh)
{
    delete meh;
    meh = nullptr;
}

Теперь мне интересно о логическом. Когда вы проходите, просто скажите int в оператор if, как это,

int meh;
if(meh)

Затем он неявно проверяет нуль, не требуя его записи.

if(meh == 0) // does the exact same check

Теперь, С++ сделает то же самое для указателей? Если передать в char *, как это, в оператор if?

char * meh;
if(meh)

Тогда это будет неявно сравнивать его с nullptr? Из-за того, как долго я писал эти, если это так, на данный момент является второй натурой, чтобы проверить, действительны ли указатели перед использованием, набрав if (object *), а затем вызвав его члены. Если это не функциональность, почему бы и нет? Слишком сложно реализовать? Решите некоторые проблемы, удалив еще один крошечный способ, которым вы могли бы испортить свой код.

4b9b3361

Ответ 1

В C все, что не 0, верно. Итак, вы, безусловно, можете использовать:

if (ptrToObject) 
    ptrToObject->doSomething();

для безопасного разыменования указателей.

C++ 11 немного меняет игру, nullptr_t - это тип, экземпляр которого nullptr; представление nullptr_t зависит от конкретной реализации. Таким образом, компилятор может определить nullptr_t так, как он хочет. Нужно только убедиться, что он может обеспечить надлежащее ограничение на приведение nullptr_t к различным типам - для которых допускается логическое значение - и убедиться, что он может различать nullptr_t и 0.

Таким образом, nullptr будет правильно и неявно приведен к логическому false, если компилятор следует спецификации языка C++ 11. И приведенный выше фрагмент все еще работает.

Если вы удалите ссылочный объект, ничего не изменится.

delete ptrToObject;
assert(ptrToObject);
ptrToObject = nullptr;
assert(!ptrToObject);    

Из-за того, как долго я пишу такие if-ы, это вторая задача - проверить правильность указателей перед использованием, введя if (object *) и затем вызывая его члены.

Нет. Пожалуйста, поддерживайте правильный график объектов (желательно используя уникальные/умные указатели). Как указано, нет способа определить, указывает ли указатель, который не является nullptr, на действительный объект или нет. В любом случае на вас лежит ответственность за поддержание жизненного цикла... вот почему оболочки указателей существуют в первую очередь.

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

Общий

#include <iostream>
#include <memory>

void report(std::shared_ptr<int> ptr) 
{
    if (ptr) {
        std::cout << "*ptr=" << *ptr << "\n";
    } else {
        std::cout << "ptr is not a valid pointer.\n";
    }
}

int main()
{
    std::shared_ptr<int> ptr;
    report(ptr);

    ptr = std::make_shared<int>(7);
    report(ptr);
}

Слабое

#include <iostream>
#include <memory>

void observe(std::weak_ptr<int> weak) 
{
    if (auto observe = weak.lock()) {
        std::cout << "\tobserve() able to lock weak_ptr<>, value=" << *observe << "\n";
    } else {
        std::cout << "\tobserve() unable to lock weak_ptr<>\n";
    }
}

int main()
{
    std::weak_ptr<int> weak;
    std::cout << "weak_ptr<> not yet initialized\n";
    observe(weak);

    {
        auto shared = std::make_shared<int>(42);
        weak = shared;
        std::cout << "weak_ptr<> initialized with shared_ptr.\n";
        observe(weak);
    }

    std::cout << "shared_ptr<> has been destructed due to scope exit.\n";
    observe(weak);
}

Теперь, C++ будет делать то же самое для указателей? Если передать символ *, например, в оператор if?

Итак, чтобы ответить на вопрос: с голыми указателями, нет. С завернутыми указателями, да.

Оберните ваши указатели, ребята.

Ответ 2

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

Если meh является необработанным указателем, то между if (meh) и if (meh != 0) и if (meh != nullptr) нет никакой разницы. Все они выполняются, если указатель не равен нулю.

Существует неявное преобразование из литерала 0 в nullptr.