Ситуация: мой код использует класс указателя подсчета ссылок (по духу похожий на boost :: intrusive_ptr ) для управления его динамическими распределениями и предотвращения утечек памяти.
AFAICT это прекрасно работает и не пропускает память; однако, когда я запускаю Clang Static Analyzer для своего кода, использующего этот класс, Clang, похоже, не понимает логику подсчета ссылок и поэтому выдает предупреждения об утечках памяти и ошибках использования после освобождения.
Приведенный ниже игрушечный код (который значительно упрощен для ясности) демонстрирует проблему - он компилируется и запускается без утечки памяти (как указано при запуске alloc-object-count, он печатает до stdout
, как и прежде), но если Я запускаю scan-build
на нем, я получаю этот вывод:
$ scan-build g++ testrefcount.cpp -o testrefcount
scan-build: Using '/Users/jaf/checker-279/bin/clang' for static analysis
testrefcount.cpp:43:54: warning: Use of memory after it is freed
const MyRefCountedObject * GetObject() const {return _obj;}
^~~~~~~~~~~
testrefcount.cpp:52:8: warning: Potential memory leak
}
^
2 warnings generated.
scan-build: 2 bugs found.
scan-build: Run 'scan-view /var/folders/q9/9w3p5x650wgfpbcws3zhsc6h0000gn/T/scan-build-2019-01-12-122209-5219-1' to examine bug reports.
У меня вопрос: есть ли способ изменить мой код так, чтобы Статический анализатор Clang не генерировал здесь ложных срабатываний? Я полагаю, что я могу просто обернуть методы подсчета рефенсов в #ifndef __clang_analyzer__
, но это кажется уродливым решением (аналогично наложению ленты на индикатор «механизм проверки»), которое не позволило бы CSA обнаруживать какие-либо реальные проблемы с этим кодом, поэтому Я предпочел бы избежать этого, если есть лучший подход. (Да, я знаю о shared_ptr
и intrusive_ptr
и так далее - интересно, как они справляются с этой проблемой?)
Ниже приведен автономный пример кода:
#include <stdio.h>
// This value will keep track of the number of MyRefCountedObject objects alive in
// the process -- just to verify that this program doesn't actually have a memory leak
static int _objectCounter = 0;
// Objects of this class will be reference-counted by the MyRef class
class MyRefCountedObject
{
public:
MyRefCountedObject(int value) : _value(value), _refCount(0)
{
printf("MyRefCountedObject %p with value %i created. Current number of MyRefCountedObjects is now %i\n", this, _value, ++_objectCounter);
}
~MyRefCountedObject()
{
printf("MyRefCountedObject %p with value %i destroyed. Current number of MyRefCountedObjects is now %i\n", this, _value, --_objectCounter);
}
int GetValue() const {return _value;}
inline void IncrementRefCount() const {_refCount++;}
inline bool DecrementRefCount() const {return (--_refCount == 0);}
private:
inline MyRefCountedObject &operator=(const MyRefCountedObject &); // deliberately unimplemented
const int _value; // An arbitrary payload/data value
mutable int _refCount; // how my MyRef's are currently pointing at this object
};
// Represents one reference to a MyRefCountedObject (like a toy shared_ptr)
class MyRef
{
public:
MyRef(const MyRefCountedObject * item = NULL) : _obj(item) {RefObject();}
MyRef(const MyRef & rhs) : _obj(NULL) {*this = rhs;}
~MyRef() {UnrefObject();}
inline MyRef &operator=(const MyRef & rhs) {this->SetRef(rhs._obj); return *this;}
const MyRefCountedObject * GetObject() const {return _obj;}
private:
void RefObject() {if (_obj) _obj->IncrementRefCount();}
void UnrefObject()
{
if ((_obj)&&(_obj->DecrementRefCount())) delete _obj;
_obj = NULL;
}
void SetRef(const MyRefCountedObject * newObj)
{
if (newObj != _obj)
{
UnrefObject();
_obj = newObj;
RefObject();
}
}
const MyRefCountedObject * _obj;
};
int main(void)
{
MyRef ref;
{
MyRef ref2(new MyRefCountedObject(555));
ref = ref2;
}
printf("At end of main, (ref)'s value is: %i\n", ref.GetObject()->GetValue());
return 0;
}