Clang Analyzer по пользовательскому общему указателю - PullRequest
0 голосов
/ 24 ноября 2018

Статический анализатор Clang сообщает об использовании памяти после ее освобождения с последующей реализацией общего ptr (довольно наивно, но все равно должно работать нормально):

template <class T>
class Shared
{
public:
    Shared()
       : ptr(0) , count(0)
    {
    }

explicit Shared(T* otherPtr)
    : ptr(0), count(0)
{
    init(otherPtr);
}

template <class DerivedT>
explicit Shared(DerivedT* otherPtr)
    : ptr(0), count(0)
{
    init(otherPtr);
}

explicit Shared(OutRef<T> ref)
    : ptr(0), count(0)
{
    init(ref.ptr);
}

Shared(const Shared& other)
    : ptr(other.ptr), count(other.count)
{
    addShared();
}

template <class DerivedT>
Shared(const Shared<DerivedT>& other)
    : ptr(other.ptr), count(other.count)
{
    addShared();
}

Shared& operator = (const Shared& other)
{
    Shared tmp(other);
    swap(tmp);
    return *this;
}

template <class DerivedT>
Shared& operator = (const Shared<DerivedT>& other)
{
    Shared tmp(other);
    swap(tmp);
    return *this;
}

~Shared()
{
    destroy();
}

operator bool() const
{
    return ptr != 0;
}

void operator = (T* otherPtr)
{
    set(otherPtr);
}

template <class DerivedT>
void operator = (DerivedT* otherPtr)
{
    set(otherPtr);
}

void operator = (OutRef<T> ref)
{
    set(ref.ptr);
}

T* operator->()
{
    return ptr;
}

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

T& operator*()
{
    return *ptr;
}

const T& operator*() const
{
    return *ptr;
}

template <class DerivedT>
void set(DerivedT* otherPtr)
{
    if (ptr != otherPtr)
    {
        destroy();
        init(otherPtr);
    }
}

T* get()
{
    return ptr;
}

const T* get() const
{
    return ptr;
}

operator T*()
{
    return ptr;
}

operator const T*() const
{
    return ptr;
}

template <class TT>
Shared<TT> cast()
{
    Shared<TT> result;
    result.ptr = (TT*)ptr;
    result.count = count;
    result.addShared();
    return result;
}

bool isLastOwner() const
{
    return count && *count == 1;
}

private:
void addShared()
{       
    if (count)
    {
        *count += 1;
    }
}

template <class DerivedT>
void init(DerivedT* otherPtr)
{
    presume(ptr == 0);
    presume(count == 0);
    if (otherPtr == 0)
    {
        return;
    }
    ptr = otherPtr;
    count = new Index(1);
}

void destroy()
{
    if (count)
    {
        *count -= 1;

        if (*count == 0)
        {
            presume(sizeof(T) > 0); //will break compilation in case of undefined T
            delete ptr;
            delete count;
        }
    }
    ptr = 0;
    count = 0;
}

void swap(Shared& other)
{
    Swap(ptr, other.ptr);
    Swap(count, other.count);
}

private:
   T* ptr;
   Index* count;

   template <class OtherT> friend class Shared;
};

Например, если у меня есть функция Shared f() и использовать его как

Shared<X> a = f();
a->callSomeMethod();

Я получаю предупреждение статического анализатора о том, что после освобождения памяти используется «a».По сути, он обнаруживает, что метод destroy () был вызван, но не может обнаружить положительный счетчик (он записывает «Предполагая, что условие истинно» для проверки нулевого счетчика) в трассировке стека.Можно ли добавить некоторый assert (), чтобы он понимал, что счетчик здесь положительный?Или, может быть, это действительно неверная реализация общего указателя?

...