Лучшие практики для проверки указателей? - PullRequest
0 голосов
/ 10 октября 2018

Итак, когда я обнаружил класс shared_ptr (и его weak_ptr брат), я полюбил его.Но потом один из наших парней сказал, что это слишком тяжело и дорого.Так что теперь у нас есть только сырые указатели и довольно тяжелый многопоточный конвейер.Из-за ошибок у нас есть ряд сбоев, которые нельзя защитить от нуля, потому что указатели на самом деле не являются нулевыми (но они являются мусором).Я думал о следующих конструкциях:

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 голосов
/ 11 октября 2018

Помимо всего прочего, ваш код имеет неопределенное поведение:

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

Проблема в том, что _ptr является копией test, а удаление test делает недействительным значение указателя.

В C ++ 98 [expr.delete] говорит:

приведенное выражение в delete-выражение должно оцениваться ровно один раз.Если выражение-удаления вызывает функцию освобождения реализации (3.7.3.2), и если операнд выражения удаления не является константой нулевого указателя, функция освобождения освободит память, на которую ссылается указатель что делает указатель недействительным .[ Примечание: значение указателя, которое относится к освобожденному хранилищу, является неопределенным.]

Или в это C ++ 11 черновик , [basic.stc.dynamic.deallocation]:

Если аргумент, данный функции освобождения в стандартной библиотеке, является указателем, который не является нулевым значением указателя (4.10), функция освобождения должна освободить хранилище, на которое ссылается указатель, делает недействительными все указатели, ссылающиеся налюбая часть освобожденного хранилища .Эффект от использования недопустимого значения указателя (включая передачу его в функцию освобождения) не определен.

(все выделено мной.)

После delete test, простопросмотр значения указателя вызывает неопределенное поведение.

...