Visual Studio 2019预览版2中的终身配置文件更新

作为C ++核心指南的一部分的C ++核心指南的生存期配置文件旨在检测生命周期问题,例如C ++代码中的悬空指针和引用。 它使用源中已经存在的类型信息以及函数之间的一些简单协定来在编译时以最少的注释检测缺陷。





原始博客

这些是概要文件期望代码遵循的基本合同:


  1. 不要使用可能悬挂的指针。
  2. 不要将可能悬挂的指针传递给另一个函数。
  3. 不要从任何函数返回可能悬挂的指针。

有关配置文件的历史记录和目标的更多信息,请查看Herb Sutter的有关1.0版的博客文章


Visual Studio 2019预览版2的新增功能


在Preview 2中,我们提供了Lifetime Profile Checker的预览版本,该版本实现了Lifetime Profile发布版本 。 此检查器是Visual Studio中C ++核心检查器的一部分。


  • 支持迭代器,string_views和spans。
  • 更好地检测自定义所有者和指针类型,这允许行为类似于容器,拥有指针或非拥有指针的自定义类型参与分析。
  • 函数调用前后条件的类型感知默认规则有助于减少误报并提高准确性。
  • 更好地支持聚合类型。
  • 总体正确性和性能改进。
  • 一些简单的nullptr分析。

启用生命周期配置文件检查器规则


默认情况下不启用检查程序规则。 如果要尝试新规则,则必须更新为项目选择的代码分析规则集。 您可以选择“ C ++ Core Check Lifetime Rules”(仅启用Lifetime Profile规则),也可以修改现有规则集以启用警告26486至26489。


“代码分析”属性页的屏幕快照,其中显示了选定的“ C ++核心检查生命周期规则”规则集。

“代码分析”属性页的屏幕快照,其中显示了选定的“ C ++核心检查生命周期规则”规则集。


运行代码分析(“分析”>“运行代码分析”)时,警告将显示在“错误列表”中;或者,如果启用了“后台代码分析”,则寿命错误将在编辑器中显示为绿色的花键。


该屏幕截图显示了“生命周期配置文件检查器”警告,并在源代码中带有绿色的花样。

该屏幕截图显示了“生命周期配置文件检查器”警告,并在源代码中带有绿色的花样。


例子


悬空指针


最简单的示例-使用悬挂指针-是最佳起点。 此处px指向x ,然后x离开范围,使px悬空。 使用px ,会发出警告。


 void simple_test() { int* px; { int x = 0; px = &x; } *px = 1; // error, dangling pointer to 'x' } 

悬空输出指针


也不允许返回悬空指针。 在这种情况下,假定参数ppx是输出参数。 在这种情况下,它设置为指向x ,该x在函数末尾超出范围。 这使*ppx悬空。


 void out_parameter(int x, int** ppx) // *ppx points to 'x' which is invalid { *ppx = &x; } 

悬挂线视图


最后两个示例很明显,但是临时实例可能会引入一些细微的错误。 您可以在以下代码中找到该错误吗?


 std::string get_string(); void dangling_string_view() { std::string_view sv = get_string(); auto c = sv.at(0); } 

在这种情况下,使用从get_string()返回的临时字符串实例构造字符串视图sv 。 然后,临时字符串将被销毁,从而使字符串视图引用无效的对象。


悬空迭代器


在容器中使用无效的迭代器时,还会发生另一个难以发现的生命周期问题。 在以下情况下,对push_back的调用可能导致向量重新分配其基础存储,从而使迭代器无效。


 void dangling_iterator() { std::vector<int> v = { 1, 2, 3 }; auto it = v.begin(); *it = 0; // ok, iterator is valid v.push_back(4); *it = 0; // error, using an invalid iterator } 

关于此示例要注意的一件事是'std :: vector :: push_back'没有特殊处理。 此行为不属于默认配置文件规则。 一个规则将容器分类为“所有者”。 然后,当在Owner上调用非const方法时,假定其拥有的内存无效,并且指向该拥有的内存的迭代器也被视为无效。


修改后的所有者


该配置文件在其指南中具有规定性。 期望您在定义函数参数时代码习惯使用类型系统。 在下一个示例中,“所有者”类型std::unique_ptr通过非const引用传递给另一个函数。 根据配置文件的规则,假定通过非常量引用传递的所有者被被调用者修改。


 void use_unique_ptr(std::unique_ptr<int>& upRef); void assumes_modification() { auto unique = std::make_unique<int>(0); // Line A auto ptr = unique.get(); *ptr = 10; // ok, ptr is valid use_unique_ptr(unique); *ptr = 10; // error, dangling pointer to the memory held by 'unique' at Line A } 

在此示例中,我们获得了指向unique拥有的内存的原始指针ptr 。 然后,非常量引用将unique性传递给函数use_unique_ptr 。 由于这是函数可以执行任何操作的unique的非常量使用,因此分析假定unique '某种程度上是无效的(例如unique_ptr :: reset),这将导致ptr悬空。


更多例子


分析还可以检测到许多其他情况。 在Visual Studio中使用您自己的代码进行尝试,然后查看发现的内容。 另外,请访问Herb的博客以获取更多示例,如果您感到好奇,请通读Lifetime Profile文件。


已知问题


如Lifetime Profile文件中所述,当前的实现不完全支持分析。 以下是此版本中未实现的主要类别。


  • 注释 -本文介绍了不支持的注释(即[[gsl::lifetime-const]] )。 实际上,这意味着,如果默认分析规则不适用于您的代码,则除了抑制误报之外,您无能为力。
  • 异常 -当前未分析异常处理路径,包括catch块的内容。
  • STL类型的 默认规则 -代替lifetime-const注释,本文建议对于我们要覆盖默认值的罕见STL容器成员函数,将它们视为已注释。 例如, std::vector::at一个重载不是const因为它可以返回非const引用-但是,我们知道调用它是lifetime-const因为它不会使向量的内存无效。 我们尚未完成对所有STL容器类型进行隐式注释的工作。
  • Lambda捕获 -如果通过lambda中的引用捕获堆栈变量,则我们目前无法检测到lambda是否离开捕获变量的范围。

     auto lambda_test() { int x; auto captures_x = [&x] { return x; }; return captures_x; // returns a dangling reference to 'x' } 

总结


在Visual Studio 2019预览版2中试用Lifetime Profile Checker。我们希望它可以帮助您识别项目中的生命周期问题。 如果您发现误报或误报,请报告它们,以便我们可以优先考虑对您而言重要的方案。 如果您对此检查(或任何Visual Studio功能)有建议或问题,请报告问题或在Developer Community上发布并告知我们。 我们还在Twitter上@VisualC

Source: https://habr.com/ru/post/zh-CN437660/


All Articles