指针验证的最佳做法?

时间:2018-10-10 20:45:08

标签: c++ shared-ptr

因此,当我发现shared_ptr类(及其weak_ptr兄弟)时,我很喜欢它。但是后来,我们中的一个人说它太重和太贵了。所以现在我们只剩下原始指针了,还有一个相当繁重的多线程管道。由于存在错误,我们遇到了许多无法进行null保护的崩溃,因为指针实际上并非为null(但是它们是垃圾)。我想到了以下构造:

template<class T>
class GoodPtr {
public:
    GoodPtr() {
        std::lock_guard<std::mutex> lock(mux);
        goodPtrs.emplace((T*)this);
    }

    virtual ~GoodPtr() {
        std::lock_guard<std::mutex> lock(mux);
        goodPtrs.erase((T*)this);
    }

    static bool isGoodPtr(T* ptr) {
        // would love to use shared_mutex, but apparently it's not in c++11, so...
        std::lock_guard<std::mutex> lock(mux);
        return goodPtrs.find((T*)ptr) != goodPtrs.end();
    }

private:
    static std::unordered_set<T*> goodPtrs;
    static std::mutex mux;
};

template<class T> std::unordered_set<T*> GoodPtr<T>::goodPtrs;
template<class T> std::mutex GoodPtr<T>::mux;

template <class T>
class GoodPtrHolder {
public:
    GoodPtrHolder(){}
    GoodPtrHolder(T* ptr) : _ptr(ptr) {}
    void setPtr(T* ptr) {
        _ptr = ptr;
    }

    T* operator->() {
        if (_ptr && GoodPtr<T>::isGoodPtr(_ptr)) {
            return _ptr;
        } else {
            static T dummy;
            return &dummy;
        }
    }

private:
    T* _ptr = nullptr;
};

给出以下示例:

class testo : public GoodPtr<testo> {
public:
    testo() {}
    virtual ~testo(){}
    void doSomething() {
        printf("sup %d\n", x);
    }
    void setX(int val) { x = val; }

private:
    int x;
};

和:

auto *test = new testo;
GoodPtrHolder<testo> testHolder(test);
testHolder->setX(5);
testHolder->doSomething();
delete test;
testHolder->doSomething();

考虑到我必须保留静态的指针表,这比shared_ptr好还是坏?有解决这个问题的更好方法吗?

1 个答案:

答案 0 :(得分:0)

除了其他所有内容之外,您的代码在这里还有未定义的行为:

delete test;
testHolder->doSomething();
// ends up here:
        if (_ptr && GoodPtr<T>::isGoodPtr(_ptr)) {
//          ^^^^

问题在于_ptrtest的副本,删除test会使指针值无效。

在C ++ 98中,[expr.delete]说:

  
      
  1. 删除表达式中的 cast-expression 应该被精确评估一次。如果 delete-expression 调用实现释放函数(3.7.3.2),并且delete表达式的操作数不是空指针常量,则释放函数将释放指针< strong>因此使指针无效。 [注意:指向释放内存的指针的值不确定。 ]
  2.   

或者在this C++11 draft[basic.stc.dynamic.deallocation]中:

  
      
  1. 如果在标准库中提供给释放函数的参数是不是空指针值(4.10)的指针,则释放函数应释放该指针所引用的存储,使所有引用的指针无效已分配存储空间 的任何部分。使用无效的指针值(包括将其传递给释放函数)的效果是不确定的。
  2.   

(全部为我的重点。)

delete test之后,仅查看指针值会调用未定义的行为。