C ++重载或监视解除引用操作符(在指针上)

时间:2011-04-20 08:11:47

标签: c++ operator-overloading

我想知道是否可以在取消引用指针时添加代码(实际指向的对象的有效性检查)。 我在重载运算符上看到了很多主题 - >,但似乎操作符是在对象上调用的,而不是指针。也许(可能)有一些我误解的事情。

这是一个例子:

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

“检查代码”应独立于所调用的函数(或成员)。我的想法是在类中将一个成员设置为一个int,并在构造(和销毁)时给它一个定义的值,然后检查该值。

您可能已经猜到我会尝试检查使用的无效指针。我尝试阅读代码,但它太大而复杂,不会错过很多潜在的错误。

感谢您的回答和见解。

4 个答案:

答案 0 :(得分:3)

operator->只能作为类的成员函数重载,而不能作为普通指针重载。

通常,无法检查(非空)指针实际上是否指向有效对象。在您的示例中,delete pObj;不会更改指针;它只是让它指向无效的内存,并没有办法测试它。所以,即使你可以在这里重载operator->,它能做的最好的事情就是检查它是否为空。

通常的方法是使用智能指针,而不是普通的指针。智能指针是一个包含指向对象的普通指针的类,并且具有operator*operator->的重载,因此它看起来像一个指针。您不会直接删除对象,而是通过指针(当它超出范围时,或通过调用reset()函数显式删除),然后指针可以在发生这种情况时将其普通指针设置为null。这样,普通指针将始终有效或为null,因此重载运算符可以在解除引用之前对其进行有效检查。

智能指针(以及一般的RAII)也带来了其他优势:自动内存管理和异常安全。在您的代码中,如果DoStuff()抛出异常,则会发生内存泄漏; pObj将超出范围,因此无法访问它以删除它指向的对象。内存将丢失,如果这种情况不断发生,您最终将使用系统的所有内存,无论是崩溃还是缓慢爬行。如果它是一个智能指针,那么该对象将在超出范围时自动删除。

标准库和Boost中常用的智能指针是auto_ptrscoped_ptrshared_ptr,每个指针在复制指针时都有不同的行为。 C ++ 0x将引入unique_ptr来替换auto_ptr

答案 1 :(得分:0)

正如尼克已经指出的那样,使用std :: auto_ptr或更好(如果可能的话)使用boost :: shared_ptr。它基本上实现了你想要的几乎。

直接回答这个问题:的确,你只能重载operator->对于一个类,所以在这种情况下,你将无法将它应用于该类的指针。换句话说,它将适用于对象,而不是指针。

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.
}

答案 2 :(得分:0)

operator ->(或任何其他运算符)只能为类对象而不是指针重载。对于您的情况,您可以考虑使用标准/ cutom 智能指针(实际上是对象而不是指针,但它们可以用作指针)。

如果无法使用智能指针,请在NULL之后进行delete/free分配。或者您可以将自定义包装用于delete,例如:

template<typename T>
void Destroy (T *&p)  // use this wrapper instead of 'delete'
{
  delete p;
  p = 0;
}

答案 3 :(得分:0)

你应该重载运算符 - &gt;和*,或多或少与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;
};

这可能是一个非常基本的例子。 - &gt;运算符会以类似的方式重载。在解除引用指针之前要执行的所有逻辑都将在preConditions()内编码。我想你可以从这里得到这个想法,如果没有,你可以进一步询问。

希望这有帮助。