情况:我的代码使用引用计数指针类(本质上类似于boost::intrusive_ptr)来管理其动态分配并避免内存泄漏。
AFAICT可以正常工作,并且不会泄漏内存;但是,当我在使用此类的代码上运行Clang静态分析器时,Clang似乎无法理解引用计数逻辑,因此会生成有关内存泄漏和释放后使用错误的警告。
下面的玩具代码(为清楚起见已大大简化)展示了该问题-编译并运行时没有泄漏内存(如运行时分配给它的对象打印到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;
}