Перегрузка C ++ или мониторинг оператора разыменования (по указателю) - PullRequest
2 голосов
/ 20 апреля 2011

Мне было интересно, возможно ли добавить код (проверяет действительность объекта, на который указывает фактически), когда указатель разыменовывается. Я видел много тем о перегрузке operator ->, но кажется, что оператор был вызван для объекта, а не для указателя. Может быть (вероятно) есть кое-что, что я неправильно понимаю.

вот пример:

T* pObj = new T();
pObj->DoStuff();    // call check code (not in DoStuff)
delete pObj;
pObj->DoOtherStuff();  // call check code (not in DoOtherStuff)

«контрольный код» должен быть независимым от вызываемой функции (или членов). Моя идея состояла в том, чтобы установить член как int в классе, и дать ему определенное значение при построении (и уничтожении), а затем проверить значение.

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

Спасибо за ваши ответы и идеи.

Ответы [ 4 ]

3 голосов
/ 20 апреля 2011

operator-> может быть перегружено только как функция-член класса, но не для обычного указателя.

Как правило, нет способа проверить, что (ненулевой) указатель действительно указывает на допустимый объект. В вашем примере delete pObj; ничего не делает для изменения указателя; он просто указывает на недействительную память, и нет способа проверить это. Таким образом, даже если бы вы могли перегрузить operator-> здесь, лучшее, что он мог сделать, это проверить, что оно не равно нулю.

Обычный подход заключается в использовании умных указателей, а не обычных указателей. Интеллектуальный указатель - это класс, который оборачивает простой указатель на объект и имеет перегрузки operator* и operator->, так что он выглядит и ощущается как указатель. Вы не будете удалять объект напрямую, но через указатель (когда он выходит из области видимости или явно, вызывая функцию reset()), и указатель может затем установить его простой указатель на ноль, когда это произойдет. Таким образом, простой указатель всегда будет либо действительным, либо нулевым, поэтому перегруженные операторы могут полезно проверить его перед разыменованием.

Умные указатели (и RAII в целом) также предоставляют другие преимущества: автоматическое управление памятью и безопасность исключений. В вашем коде произойдет утечка памяти, если DoStuff() сгенерирует исключение; pObj выйдет из области видимости, и поэтому не будет никакого доступа к нему, чтобы удалить объект, на который он указывает. Память будет потеряна, и, если это будет продолжаться, вы в конечном итоге будете использовать всю системную память и либо зависать, либо медленно сканировать. Если бы это был умный указатель, то объект был бы удален автоматически, поскольку он вышел из области видимости.

Обычно используются интеллектуальные указатели из стандартной библиотеки и Boost auto_ptr, scoped_ptr и shared_ptr, каждый из которых отличается при копировании указателя. C ++ 0x представит unique_ptr вместо auto_ptr.

0 голосов
/ 20 апреля 2011

Вы должны перегружать операторы -> и * более или менее так же, как работает auto_ptr. Например:

template<typename T>
class SafePtr {
public:
    SafePtr(T*p) : ptr(p) {}
    T &operator*()
    {
        if ( !preConditions() ) {
            throw runtime_error( "preconditions not met" );
        }

        return *ptr;
    }
    T * operator->()
    {
        if ( !preConditions() ) {
            throw runtime_error( "preconditions not met" );
        } 

        return ptr;
    }
    bool preConditions()
    { return ( ptr != NULL ); }

private:
    T* ptr;
};

Это может быть очень простой пример. Оператор -> будет перегружен аналогичным образом. Вся логика, которую вы хотите выполнить перед разыменованием указателя, будет закодирована внутри preConditions (). Я думаю, что вы можете получить идею отсюда, если нет, вы можете спросить дальше.

Надеюсь, это поможет.

0 голосов
/ 20 апреля 2011

operator -> (или любые другие операторы) могут быть перегружены только для объектов класса, а не для указателей. В вашем случае вы можете подумать об использовании стандартных / cutom Smart Pointers (которые на самом деле являются объектами, а не указателями, но могут использоваться как указатели).

Если нельзя использовать умные указатели, тогда попрактикуйтесь в присвоении NULL после delete/free. Или вы можете использовать свою пользовательскую оболочку для delete, например:

template<typename T>
void Destroy (T *&p)  // use this wrapper instead of 'delete'
{
  delete p;
  p = 0;
}
0 голосов
/ 20 апреля 2011

Как уже указал Ник, используйте std :: auto_ptr или лучше (и, если возможно,) boost :: shared_ptr. Он в основном реализует почти то, что вы хотите.

Чтобы ответить на вопрос напрямую: вы можете перегрузить только оператор-> для класса, поэтому в этом случае вы не сможете применить его к указателю этого класса. Другими словами, он будет применяться к объектам, а не к указателям.

class T {
  T& operator->() { }
}; 

void f() {
  T* pObj = new T();
  pObj->DoStuff(); // Calls DoStuff, but... Oops! T::operator->() was not called! 
  (*pObj).DoStuff(); // Equivalent to the above
  delete pObj;
  (*pObj)->DoStuff(); // T::operator->() is called, but
      // there is no improvement here: the pointer is dereferenced 
      // earlier and since it was deleted, this will "crash" the application
  //pObj->->DoStuff(); // Syntactically incorrect, but semantically equivalent 
     //to the above
  pObj->operator->()->DoStuff(); // Semantically equivalent to the above two,
     //but avoids the double member access operator.
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...