Как я понимаю Основные руководящие принципы 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.