是否可以让Clang Static Analyzer理解引用计数?

时间:2019-01-12 20:26:43

标签: c++ reference-counting clang-static-analyzer

情况:我的代码使用引用计数指针类(本质上类似于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_ptrintrusive_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;
}

0 个答案:

没有答案