Есть ли / должен ли быть способ отследить валидность необработанного указателя, взятого из умного указателя для целей отладки / контроля качества? - PullRequest
0 голосов
/ 11 мая 2019

Как я понимаю Основные руководящие принципы C ++ Бьярна Страуструпа и Херба Саттера , неплохо использовать указатели следующим образом:

  • unique_ptr для старого доброго владельца
  • shared_ptr для действительно общего владения по проекту
  • weak_ptr, когда по замыслу указатель может быть недействительным больше
  • необработанный указатель всякий раз, когда указатель действителен по замыслу, а владение не изменяется

Таким образом, теоретически все еще может быть распространенной ошибкой, что необработанный указатель становится недействительным (см. R37 ).
Интересно, есть ли способ для целей отладки / проверки качества сделать утверждение о достоверности необработанного указателя.Я считаю, что такие проверки могут помочь значительно улучшить стабильность программ.

Я написал следующую возможную реализацию.Мой вопрос:Что вы думаете о наличии такого кода?Это имеет смысл для вас?Существует ли что-то подобное?Любые другие комментарии?

На этом этапе незавершенность реализации не так важна (безопасность потоков или другие вещи), если только она не отражает причину, по которой это не делается.

#include <memory>
#include <cassert>

#if NDEBUG

template <typename T>
using WatchedPtr = T*;

template <typename T>
T* get (const std::shared_ptr<T>& p) {return p.get();}

#else

template <typename T>
class WatchedPtr {
    template <typename T1>
    friend WatchedPtr<T1> get (const std::shared_ptr<T1>& p);

public:
    WatchedPtr() : _ptr (nullptr) {}

    operator T* () { return getPtr();}

    T* operator->() const { assert (getPtr()); return getPtr(); }

    T& operator*() const { assert (getPtr()); return *getPtr(); }

private:
    T* getPtr () const { return _weak.expired () ? nullptr : _ptr;}
    WatchedPtr(std::shared_ptr<T> p) : _weak (p) {
        _ptr = _weak.lock().get();
    }
    std::weak_ptr<T> _weak;
    T* _ptr;
};

template <typename T1>
inline WatchedPtr<T1> get (const std::shared_ptr<T1>& p) {return WatchedPtr<T1> (p);} //because I cannot change the get() member

#endif

Пример использования:

void f (WatchedPtr<int> p) {
    assert(p);//detects errors
    *p = 3;//detects errors
    std::cout << "f: *p=" << *p << std::endl;
}

void g (int* p) {
    assert(p);//detects errors
    *p = 3;//does not detect error
    std::cout << "g: *p=" << *p << std::endl;
}

int main () {
    WatchedPtr<int> p;

    {
        auto pp = std::make_shared<int> (1234);
        p = get(pp);
        f (p);
    }
    f (p);
    g (p); //the internal assert() of g() will detect errors, because casting of invalid p to raw pointer will be nullptr
}

* Я думал о реализации также для unique_ptr.Но пока моя единственная идея - хранить указатель на unique_ptr в WatchedPtr.Это будет определять ошибки только в некоторых случаях, когда оператор get () или bool завершится сбоем или вернет nullptr.

...