Задать вопрос

Есть ли механизм проверки валидности указателей в C++?

Доброго времени суток!
Можно ли как-то в С++ проверить указатель на доступность области памяти, на которую он указывает?

Ситуация следующая. Есть dll, определяющая класс Delegate, который является представлением функции какого-либо класса (служит для колбэков). Его конструктор имеет примерно следующую сигнатуру:
template<typename T, typename ... Args> 
explicit Delegate(T* owner, (T::*function), Args ... args);

Так же перегружен operator(), что позволяет сделать примерно следующее:
class Foo
{
public:
    void func() {}
};

Foo* f = new Foo;
Delegate d(f, &Foo::func);
d(); // вызовется f->func();

В классе Delegate, в перегрузке оператора (), как проверить валидность переданного указателя в конструкторе? Сейчас, при следующем раскладе, программа крашнется:
Foo* f = new Foo;
Delegate* d = new Delegate(f, &Foo::func);
delete f;
(*d)(); // здесь будет необработанное исключение

На последней строчке произойдет обращение к недоступной области памяти. В этой части, я разрабатываю только класс Delegate, поэтому я не могу быть уверен, что пользователь после удаления указателя обнулит его. Более того, в следующей ситуации (которая и является целевой) это вроде как и невозможно:
class A
{
public:
    A() : delegate_(this, &A::funcA) {}
    Delegate callback() const { return delegate_; }
    void funcA() {}
private:
    Delegate delegate_;
};
class B
{
public:
    void setCallback(const Delegate& d)
    { delegate_ = d; }
    void event()
    { delegate_(); }
private:
    Delegate delegate_;
};
// -------------------------------------
A* classA = new A;
B* classB = new B;
classB->setCallback(classA->callback());
delete classA;
classB->event(); // печаль-беда


Собственно, как мне в классе Delegate, перед вызовом функции на переданном в конструкторе указателе, проверить его на валидность? Программа крашится с "необработанным исключением" по причине "обращения к недоступной области памяти", но даже блок try{} catch(...){} ничего не перехватывает... Как мне обработать такую ситуацию и узнать, что указатель указывает на мусор?
  • Вопрос задан
  • 5930 просмотров
Подписаться 7 Оценить 3 комментария
Решения вопроса 3
tsarevfs
@tsarevfs Куратор тега C++
C++ developer
Для отладки существует такая штука, как дебажная куча. Насколько я понимаю, в релизе адекватного варианта проверить валидность указателя быть не может. Можно попытаться как-то это обойти. Например получать shared_ptr, что намекнет пользователю что не надо раньше времени удалять объект.
Ответ написан
Собственно, как мне в классе Delegate, перед вызовом функции на переданном в конструкторе указателе, проверить его на валидность?

никак, стандартного способа нет.

Более того, в следующей ситуации (которая и является целевой) это вроде как и невозможно:

я тут вас не совсем понял, у вас целевой ситуацией является код с ошибкой? Или вы хотите как-то отловить такие ошибки, чтобы исправить? Если первое - то полная печаль, если второе, то можете попробовать статические анализаторы, cppcheck, clang scan-build, etc... Они с переменным успехом могут такие ошибки находить. Можете попробовать valgraind (точнее его замену под Windows stackoverflow.com/questions/413477/is-there-a-good... ), он тоже такие ошибки находит, но в отличие от статического анализатора, только если такая ошибка произошла при конкретном запуске.
Ответ написан
@Lol4t0
Вам нужна архитектура с контролем удаления объекта. Сделать такое можно, но сложность довольно сильно возрастает:

#include <unordered_set>


class delegate;

class delegate_object_impl
{
public:
    void setDelegate(delegate* delegate)
    {
        _d.insert(delegate);
    }

    void clearDelegate(delegate* delegate)
    {
        _d.erase(delegate);
    }
    virtual ~delegate_object_impl();

private:
    std::unordered_set<delegate*> _d;
};

template <typename T>
struct delegate_object: T, delegate_object_impl
{
    template<typename...Args>
    delegate_object(Args... args):
        T(args...)
    {}
};

class delegate
{
public:
    template<typename T, typename ... Args> 
    explicit delegate(delegate_object<T>* owner, void(T::*)(), Args ... args):
        _owner(owner)
    {
        //...
        owner->setDelegate(this);
    }

    delegate_object_impl* _owner;

    ~delegate()
    {
        if (_owner) {
            _owner->clearDelegate(this);
        }
    }

    void operator()()
    {

    }

    void forget()
    {
        _owner = 0;
    }
};

delegate_object_impl::~delegate_object_impl()
{
    for (delegate* d : _d) {
        d->forget();
    }
}


Понятное дело, что городить такое имеет смысл только тогда, когда ситуация, приведенная вами, является не ошибкой программиста а частью нормального жизненного цикла системы.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@xandox
То что ты хочешь сделать не возможно, да и не нужно, если пользователь отдал тебе указатель, а потом освободил его - то он сам дурак и с этим ничего не поделать. Единственное, что я тебе бы посоветовал использовать shared_ptr, что бы пользователю было бы легче следить за временем жизни своего объекта. В принципе тебе можно хранить weak_ptr и при вызове проверять - не протух ли он.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы