是否有调试机制、解决方法、包装器或工具来检测shared_ptr周期



我目前的环境是Visual Studio,虽然我的项目是跨平台的,但我更喜欢为windows平台找出一些东西,因为在另一个操作系统上设置它需要一两天的工作来收集外部库和依赖项并设置项目。

我已经使用c++ 11的特性有一段时间了,通常对对象所有权很好(我还没有遇到过很多所有权可以循环的情况)

不幸的是,我目前正试图追踪一个给定对象的shared_ptr副本的起源(行号,shared_ptr创建id或拥有对象),我希望这些对象应该被释放,但它们以某种方式存在于一个循环中。

我在某个地方违反了所有权契约(可能在lambda中捕获了shared_ptr并延长了其生命周期)。

我查看了堆栈溢出和在线,常见的重复是"重新审视您的整个设计",这基本上是一个没有答案的问题(这也是我首先发布这篇文章的原因)。我承认我的设计有一个类似于内存泄漏的问题(有哪些工具可以检测),我承认这个问题的存在是我的错。我只是想以一种合理的方式缩小的范围。

似乎如果我能以某种方式访问shared_ptr构造函数和析析函数,我可以通过某种内部guid打印调试信息,这可能有助于缩小确切的问题(如果我知道创建了id为30的shared_ptr,但析析函数没有被击中,我可以在第二次运行时,在创建具有该id的shared_ptr的精确位置上放置一个断点,这将是一个语言级别的调试工具)。我认为,另一种选择是,如果某种内存分析器可以确定特定shared_ptr的每个活动实例的起始行号。

我还继承了enable_shared_from_this,如果这很重要。

对于追踪这个问题有什么建议吗?

继承shared_ptr是一个坏主意,因为shared_ptr没有虚析构函数。我假设您了解RAII和所有这些内容,因此我建议跟踪它的最佳方法是将shared_ptr封装在结构体或类中。比如:

template<typename T>
struct shared_ptr_wrapper{
shared_ptr<T> ptr;
int line;
shared_ptr_wrapper<T>(T& obj, const int selected_line)
{
     //Initialization as normal. Maybe even print out the address of the pointer
}
//Relevant access functions like constructing, destructing 
//and changing references
};

***有趣的是,如果你想以编程方式向这个类添加一些信息,c++提供了编译时常量__LINE__,它指向被称为的行号,这可以使你的调试过程比设置断点更容易,因为对象本身就有这个信息。

你不会有语法糖,你可以有一个普通的shared_ptr,但这将在调试方面做得更好。如果需要,您可以虚拟地组织构造函数以提供合适的子类化环境,原因如下

virtual ~shared_ptr_wrapper()
{
std::cout << line; 
std::cout << ptr//May want to print the address of the shared pointer
//So that you can stop execution and take a look at the variables right before
//It is/Is not destructing
}

也可以根据具体的程序有效地使用断言。如果你认为某个变量在某一点上应该是死的,调用一个方法来断言它,如果你没有,那么你就有了答案,我有一个更快的调试工具,assert的一个稍微升级的版本告诉你行号

#define fassert(expr) lassert((expr), #expr, __FILE__, __LINE__) //This macro has been    
//tested by the coding gods greater than me.
bool lassert(bool invariant, std::string statement, std::string file, int line)
{
if(!invariant)
{
std::cerr << "fassert Failed!" << std::endl;
std::cerr << "Statement: " << statement.c_str() << std::endl;
std::cerr << "File: " << file.c_str() << std::endl;
std::cerr << "Line: " << line <<std:: endl;
abort(); 
}
return true;
}

现在您提到了lambda捕获变量。objective - c程序员使用的一个简单解决方案是创建弱引用(我对boost的文档中如何做到这一点有点模糊),并在所有lambda中使用它们。这可能意味着你需要再次创建子类并手动删除变量;也就是说,由于您只是包装而不是子类化,您将不得不在编程循环中每隔一段时间执行所有检查,或者在一段时间间隔(可能是多线程?)

最后,"重新审视你的整个设计"严格意义上是指不改变你的整个程序,而是指当静态函数调用可以完成时避免使用lambdas,或者使用c风格的函数指针将"捕获"变量的数量保持在最小。

还有许多其他的内存技巧,但如果您保持完全相同的方式编程,很可能会出现一些其他的循环引用。出于这个原因,我建议不要使用内存分析器,因为它肯定会减慢您的程序速度,c++以其速度而闻名,并且只要您不调试包含多个实例的20个源文件项目,就会导致不必要的设置和监视。从经验来看,它们可能会变得很麻烦。作为程序员,我们都希望我们的代码运行,而不是占用我们的整个RAM。没有什么灵丹妙药,尝试几种方法或组合,看看哪种适合你。祝你调试顺利,

bv

最新更新