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

时间:2014-07-30 00:52:44

标签: c++ c++11 shared-ptr cycle

我目前的环境是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的每个 alive 实例的原始行号。

如果重要的话,我也会从enable_shared_from_this继承。

有关追踪此问题的建议吗?

1 个答案:

答案 0 :(得分:2)

好的子类化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 __ ,它指向行号的行号< / i>这可以使你的调试节比设置断点更容易,因为对象本身会有这些信息。

你没有普通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
}

根据您的具体计划,也可以相当有效地使用断言。如果你认为某个变量应该在某一点死掉,那么调用一个断言的方法,如果你没有,那么你就得到了答案,我有一个调试工具就是fassert,一个稍微升级的断言版本告诉你行号

#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;
}

现在你提到了lambdas捕获变量。客观程序员使用的一个简单解决方案是制作弱引用(我对Boosts关于如何做的文档有点模糊)并在所有lambdas中使用它们。这可能意味着您需要再次子类并手动删除变量;也就是说,由于你只是简单地换行而不是子类化,你必须在编程循环中经常执行所有检查,或者让每个指针在一段时间间隔之后自己执行(多线程可能?)

最后,“重新访问整个设计”的引用严格意味着不要更改整个程序,但这意味着在静态函数调用时要避免使用lambdas,或者使用c风格函数指针将“捕获”变量的数量保持在最小。

还有许多其他的记忆技巧,但最有可能的是,如果你继续以完全相同的方式编程,其他一些循环参考将在后面出现。因此我建议不使用内存分析器,因为它肯定会减慢你的程序速度,c ++以其速度而闻名,并且只要你没有调试多个20源文件项目,就会造成不必要的设置和监视。实例。从经验来看,它们会变得很麻烦。作为程序员,我们都希望我们的代码运行而不占用我们的整个RAM。没有银弹,尝试一些东西或组合,看看有什么效果。祝你的调试工作顺利,

-bV