Проверить определенность указателя в C++

Как проверить, определена ли переменная, в частности указатель, в C++? Допустим, у меня есть класс:

class MyClass {  
public:

    MyClass();

    ~MyClass() {
        delete pointer; // if defined!
    }

    initializePointer() {
        pointer = new OtherClass();
    }

private:

    OtherClass* pointer;

};

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

Johannes Schaub - litb 19.12.2008 21:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
1
15 825
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Я стараюсь инициализировать значения указателя NULL при создании объекта. Это позволяет проверять NULL, чтобы увидеть, определена ли указательная переменная.

Я согласен, за исключением того, что вы должны использовать 0, а не NULL. см. research.att.com/~bs/bs_faq2.html#null

Douglas Leeder 19.12.2008 21:25

Или, в C++ 0x, вы должны использовать nullptr.

Max Lybbert 19.12.2008 23:36

Я согласен, хотя «я склонен» звучит как «рекомендуется время от времени дышать».

peterchen 20.12.2008 03:07

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

MyClass::MyClass() : pointer(NULL)
{
}

MyClass::~MyClass()
{
    if (pointer != NULL) { delete pointer; }
}

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

Преимущество выполнения этого способа (вместо того, чтобы явно указывать «указатель = NULL;» в конструкторе), заключается в том, что компилятор уже неявно выполняет подобное присваивание за вас. При ручном назначении это происходит дважды, что обычно не имеет большого значения, кроме случаев, когда вы создаете много экземпляров своего класса в большом цикле или что-то в этом роде.

Ваш код бессмысленен на C++, поскольку delete уже включает проверку NULL. Не проверяйте значение null вручную перед удалением!

Konrad Rudolph 19.12.2008 21:14

@Konrad: Я бы не назвал это "бессмысленным", но определенно "лишним" и "ненужным"

James Curran 19.12.2008 21:28

Вы не можете, AFAIK я знаю. Стандартный метод - установить для него значение NULL, если он не содержит допустимого значения. Оставлять указатели вокруг этой точки на недопустимую память - плохая практика ни при каких обстоятельствах. Если вы будете придерживаться этого правила, вы всегда можете проверить значение NULL, чтобы узнать, «определено» оно или нет.

В дополнение к проверке на наличие 0 (NULL) одним из решений может быть рефакторинг вашего кода, чтобы вы сила указатель всегда оставался действительным. Это не всегда возможно, но в большинстве случаев это лучшее решение.

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

Это часто используемый шаблон в C++, и он эффективно ограничивает время жизни объекта указателя временем жизни вашего класса. Иногда жизнеспособным решением может быть предоставление некоторого типа reset, который удаляет указатель и немедленно повторно инициализирует его. Если это написано безопасным для исключений способом, вы также гарантируете, что ваш указатель никогда не будет недействительным.

Создайте ли нет флаг boolean для отслеживания действительности вашего указателя. Это решение не имеет преимуществ и имеет много недостатков по отношению к установке указателя на 0.

Ответ принят как подходящий

Зачем беспокоиться о проверке значения указателей? Просто инициализируйте его значением нулевого указателя, а затем просто вызовите для него delete. delete по нулевому указателю ничего не делает (это гарантирует стандарт).

class MyClass {  
public:

    MyClass():pointer(0) { }

    ~MyClass() {
        delete pointer;
        pointer = 0;
    }

    initializePointer() {
        pointer = new OtherClass();
    }

private:

    OtherClass* pointer;

};

И каждый раз, когда вы вызываете для него delete, вы должны установить указатель на значение нулевого указателя. Тогда у вас все в порядке.

Я обычно компилирую, когда все предупреждения рассматриваются как ошибки. У меня возникли ошибки компиляции при удалении указателей с нулевым значением.

tunnuz 19.12.2008 21:21

Как компилятор узнает, что указатель будет равен нулю во время выполнения?

James Curran 19.12.2008 21:27

Томмазо, это звучит непослушно. Я также хотел бы знать, как компилятор может узнать во время компиляции, что содержит указатель во время выполнения.

Johannes Schaub - litb 19.12.2008 21:36

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

Greg Rogers 19.12.2008 21:40

@ Грег Роджерс: здесь это не помогает, но и не повредит. В других ситуациях, когда вместо деструктора удаление происходит в другой части кода, установка указателя на 0 позволит избежать двойного удаления через эту переменную (не через другие указатели).

David Rodríguez - dribeas 19.12.2008 21:41

я не хотел показаться глупым. говоря «вы должны установить для него значение null после его удаления», а затем не делать этого, это нехорошо :) также мы могли бы добавить членов позже, которые могли бы попробовать его использовать и протестировать на ненулевое значение в своих деструкторах. поэтому я делаю «всегда делать его нулевым» вместо «делать нулевым только тогда, когда это необходимо».

Johannes Schaub - litb 19.12.2008 22:41

Попробую еще раз, возможно, мои ошибки при компиляции были вызваны другими конструкциями.

tunnuz 19.12.2008 22:44

@dribeas - Хотел бы я дать вам +1 за этот комментарий.

Marcin 19.12.2008 23:32

Это было настолько очевидно, что я упустил это! Вы только что решили для меня двухчасовую Heisenbug. Спасибо!

CodeMouse92 13.08.2015 08:26

Настоящий отвечать - это отвечать, но я просто хотел сделать дополнительный комментарий. Использование интеллектуальных указателей (в этом случае достаточно std :: auto_ptr) решает проблему и имеет то преимущество, что вам не нужно помнить об удалении указателя в деструкторе. Фактически деструктор по умолчанию (сгенерированный компилятором) позаботится о ресурсах памяти.

Если сделать комментарий еще более общим, ваш класс должен объявить оператор присваивания и конструктор копирования, помеченные как частные (и не определенные) или определенные вручную, чтобы избежать двойного удаления указателя. Компилятор предоставил operator == и конструктор копирования просто скопирует указатель, и в конечном итоге вы попадете в двойное удаление. Я пишу это здесь, так как это тоже нужно учитывать, если вы используете std :: auto_ptr с добавленной странностью семантики копирования в auto_ptr.

Дополнительный ответ, о котором еще никто не упоминал, заключается в использовании объекта интеллектуального указателя вместо необработанного указателя. В зависимости от того, как впоследствии будет использоваться указатель, здесь могут быть уместны std::auto_ptr, boost::shared_ptr или ряд других классов интеллектуальных указателей. boost::scoped_ptr также может работать, если вы немного измените принцип работы initializePointer().

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

class MyClass {  
public:

    MyClass();

    ~MyClass() {
    }

    initializePointer() {
        pointer.reset( new OtherClass());
    }

private:

    std::auto_ptr<OtherClass> pointer;

};

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

Вызов удаления для NULL хорошо определен.

В более обычном случае можно ожидать, что конструктор определит значение указателя RAW. Кроме того, поскольку ваш объект содержит указатель RAW и, очевидно, владеет им (он удаляет его, это подразумевает владение), вы ДОЛЖНЫ также убедиться, что вы переопределяете созданные компилятором версии конструктора копирования и оператора присваивания.

class MyClass
{  
    public:
        MyClass()
            :pointer(NULL)  // valid value.
        {}
        ~MyClass()
        {
            delete pointer; // This is fine.
        }

        void initializePointer() // Missing return type
        {
            pointer = new OtherClass();
        }

    private:
        MyClass(MyClass const& copy);           // If you don't define these 
        MyClass& operator=(MyClass const& copy);// two the compiler generated ones
                                                // will do nasty things with owned
                                                // RAW pointers.

        OtherClass* pointer;
};

В качестве альтернативы вы можете использовать один из стандартных интеллектуальных указателей. Вероятно, std :: auto_ptr <>, если вы действительно не хотите сделать объект копируемым.

Другие вопросы по теме