Проверьте определенность указателя в C ++ - PullRequest
4 голосов
/ 19 декабря 2008

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

class MyClass {  
public:

    MyClass();

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

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

private:

    OtherClass* pointer;

};

Ответы [ 8 ]

22 голосов
/ 19 декабря 2008

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

class MyClass {  
public:

    MyClass():pointer(0) { }

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

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

private:

    OtherClass* pointer;

};

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

8 голосов
/ 19 декабря 2008

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

6 голосов
/ 19 декабря 2008

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

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

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

Не не создайте флаг bool ean, чтобы отслеживать правильность вашего указателя. Это решение не имеет преимуществ и многих недостатков для установки указателя на 0.

4 голосов
/ 19 декабря 2008

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

Вызов delete на NULL четко определен.

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

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

2 голосов
/ 19 декабря 2008

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

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

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

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

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

1 голос
/ 19 декабря 2008

Дополнительный ответ, который еще никто не упомянул, заключается в использовании объекта интеллектуального указателя вместо необработанного указателя. В зависимости от того, как впоследствии используется указатель, здесь могут быть уместны 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;

};
1 голос
/ 19 декабря 2008

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

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

1 голос
/ 19 декабря 2008

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

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