2011-04-20 13 views
2

我在想如果在解除引用指针时是否可以添加代码(实际指向的对象的有效性检查)。 我看到很多重载operator - >的主题,但似乎操作符是在一个对象上调用的,而不是指针。也许(可能)有我误解的东西。C++过载或监视解除引用操作符(在指针上)

这里有一个例子:

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

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

正如你可能猜测我试图检查使用的无效指针。我尝试阅读代码,但它太大而且复杂,不能错过许多潜在的错误。

感谢您的回答和见解。

+2

如果你真的担心悬挂指针,然后使用一个智能指针,如boost :: shared_ptr。 – Nick 2011-04-20 08:15:51

回答

3

operator->只能重载为一个类的成员函数,而不是一个正常的指针。

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

通常的做法是使用智能指针,而不是普通指针。智能指针是一个包装指向对象的普通指针的类,它的重载为operator*operator->,因此它看起来和感觉就像一个指针。您不会直接删除对象,而是通过指针(当它超出范围时,或通过调用reset()函数明确指定),然后指针可以在发生这种情况时将其纯指针设置为空。这样,普通指针总是有效的,或者为空,所以重载操作符可以在解引用之前有效地检查它。

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

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

+1

“他最好能做的就是检查它是否为空”。在实践中,检查可能会比使用类型T的支持更好一点。向T添加一个“magicnumber”字段,在构造函数中将其设置为特殊值,并在析构函数中设置为其他值(以及确保编译器不会优化分配)。然后,如果'pObj-> magicnumber'具有任何其他值,则该对象肯定不是活的。不可移植的,当然,因为读取空闲内存有未定义的行为,但在实践中,它不可能比调用“DoOtherStuff”成员函数更糟糕。 – 2011-04-20 09:01:14

+1

...和btw可以返回误报如果内存已被重新用于相同类型的另一个对象。但没有误报。 – 2011-04-20 09:04:43

+0

感谢您的答案!这是我所害怕的。无论如何,额外的信息是非常感谢。 – Chouppy 2011-04-21 05:23:26

0

正如Nick已经指出的那样,使用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. 
} 
+0

' - > - >'在语法上真的不正确吗?我想我以前见过这个(如果我没记错的话,它实际上是一个三重的' - > - > - >')。 – 2011-04-20 08:36:13

+0

感谢您的回答。在我的情况下,问题是已经有很多代码(数十万行),所以更改所有指针非常困难(并且可能有风险)。 – Chouppy 2011-04-21 05:27:07

+0

@Giovanni Funchal,啊,你让我困惑。但这很好,因为它让我思考并尝试一下。而MSVC++ 2008在' - > - >'上给了我一个语法错误。调用'pObj->运算符 - >() - > DoStuff()'是好的。 – 2011-04-28 13:32:34

0

operator ->(或任何其它操作者)只能为类对象,而不是为了指针被重载。对于你的情况,你可以考虑使用标准/剪辑智能指针(它们实际上是对象而不是指针,但它们可以用作指针)。

如果不能使用智能指针,则在delete/free之后进行分配NULL的练习。或者你可以使用你的自定义包装这样delete

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

是的,我们在删除后将指针设置为null,但问题在于指向相同对象的程序周围有很多指针(这很杂乱,我同意,也许我们应该得到我们所拥有的;)) 。 – Chouppy 2011-04-21 05:33:44

0

您应该以与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()内编码。我认为你可以从这里得到想法,如果没有,你可以进一步询问。

希望这会有所帮助。