cppcoreguidelines-owning-memory¶
此检查实现了 gsl::owner<T*>
的基于类型的语义,允许对使用原始指针处理资源(如动态内存)的代码进行静态分析,但不会引入 RAII 概念。
此检查实现了来自 C++ 核心准则的 I.11、C.33、R.3 和 GSL.Views。 gsl::owner<T*>
的定义非常简单
namespace gsl { template <typename T> owner = T; }
因此,即使不使用 准则支持库 的实现,也很容易引入所有者。
所有检查都是纯粹基于类型的,目前还没有流敏感性。
以下示例将演示所有者的正确和不正确初始化,分配的处理方式相同。注意 new
和 malloc()
之类的资源函数都被认为会产生资源。
// Creating an owner with factory functions is checked.
gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
// Dynamic memory must be assigned to an owner
int* Something = new int(42); // BAD, will be caught
gsl::owner<int*> Owner = new int(42); // Good
gsl::owner<int*> Owner = new int[42]; // Good as well
// Returned owner must be assigned to an owner
int* Something = function_that_returns_owner(); // Bad, factory function
gsl::owner<int*> Owner = function_that_returns_owner(); // Good, result lands in owner
// Something not a resource or owner should not be assigned to owners
int Stack = 42;
gsl::owner<int*> Owned = &Stack; // Bad, not a resource assigned
在动态内存作为资源的情况下,只有 gsl::owner<T*>
变量可以被删除。
// Example Bad, non-owner as resource handle, will be caught.
int* NonOwner = new int(42); // First warning here, since new must land in an owner
delete NonOwner; // Second warning here, since only owners are allowed to be deleted
// Example Good, Ownership correctly stated
gsl::owner<int*> Owner = new int(42); // Good
delete Owner; // Good as well, statically enforced, that only owners get deleted
此外,检查将确保,期望 gsl::owner<T*>
作为参数的函数,要么使用 gsl::owner<T*>
,要么使用新创建的资源进行调用。
void expects_owner(gsl::owner<int*> o) { delete o; }
// Bad Code
int NonOwner = 42;
expects_owner(&NonOwner); // Bad, will get caught
// Good Code
gsl::owner<int*> Owner = new int(42);
expects_owner(Owner); // Good
expects_owner(new int(42)); // Good as well, recognized created resource
// Port legacy code for better resource-safety
gsl::owner<FILE*> File = fopen("my_file.txt", "rw+");
FILE* BadFile = fopen("another_file.txt", "w"); // Bad, warned
// ... use the file
fclose(File); // Ok, File is annotated as 'owner<>'
fclose(BadFile); // BadFile is not an 'owner<>', will be warned
选项¶
- LegacyResourceProducers¶
以分号分隔的旧式函数的完全限定名列表,这些函数创建资源但不能引入
gsl::owner<>
。默认值为::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile
。
- LegacyResourceConsumers¶
以分号分隔的旧式函数的完全限定名列表,这些函数期望资源所有者作为指针参数,但不能引入
gsl::owner<>
。默认值为::free;::realloc;::freopen;::fclose
。
局限性¶
在 typedef 或别名中使用 gsl::owner<T*>
处理不正确。
using heap_int = gsl::owner<int*>;
heap_int allocated = new int(42); // False positive!
gsl::owner<T*>
被声明为模板类型别名。在模板函数和类中,如以下示例所示,类型别名的信息丢失。因此,在大量模板化的代码库中使用 gsl::owner<T*>
可能会导致误报。
已知无法正确诊断的代码结构:
std::exchange
std::vector<gsl::owner<T*>>
// This template function works as expected. Type information doesn't get lost.
template <typename T>
void delete_owner(gsl::owner<T*> owned_object) {
delete owned_object; // Everything alright
}
gsl::owner<int*> function_that_returns_owner() { return gsl::owner<int*>(new int(42)); }
// Type deduction does not work for auto variables.
// This is caught by the check and will be noted accordingly.
auto OwnedObject = function_that_returns_owner(); // Type of OwnedObject will be int*
// Problematic function template that looses the typeinformation on owner
template <typename T>
void bad_template_function(T some_object) {
// This line will trigger the warning, that a non-owner is assigned to an owner
gsl::owner<T*> new_owner = some_object;
}
// Calling the function with an owner still yields a false positive.
bad_template_function(gsl::owner<int*>(new int(42)));
// The same issue occurs with templated classes like the following.
template <typename T>
class OwnedValue {
public:
const T getValue() const { return _val; }
private:
T _val;
};
// Code, that yields a false positive.
OwnedValue<gsl::owner<int*>> Owner(new int(42)); // Type deduction yield T -> int *
// False positive, getValue returns int* and not gsl::owner<int*>
gsl::owner<int*> OwnedInt = Owner.getValue();
当前实现的另一个局限性是仅基于类型的检查。假设你拥有以下代码
// Two owners with assigned resources
gsl::owner<int*> Owner1 = new int(42);
gsl::owner<int*> Owner2 = new int(42);
Owner2 = Owner1; // Conceptual Leak of initial resource of Owner2!
Owner1 = nullptr;
gsl::owner<T*>
的语义与 std::unique_ptr<T>
非常类似,因此两个 gsl::owner<T*>
的赋值被视为移动,这需要在赋值之前释放资源 Owner2
。这种类型的条件可以在该检查的后续改进中通过流敏感分析来捕获。目前,Clang 静态分析器 会捕获动态内存的此错误,但不会捕获一般类型的资源的此错误。